From a3861e644ea24241c3fdb986bc4ae98e43013ff0 Mon Sep 17 00:00:00 2001 From: Seokhyun Lee <7948302+henrylee97@users.noreply.github.com> Date: Fri, 1 Dec 2023 08:28:28 +0900 Subject: [PATCH] refactor(java): commons-lang D-classes (#135) --- Java/commons-lang-DateUtils_1133/Dockerfile | 18 + Java/commons-lang-DateUtils_1133/buggy.java | 1884 ++++++++++++++++ .../commons-lang-DateUtils_1133/metadata.json | 21 + Java/commons-lang-DateUtils_1133/npe.json | 7 + Java/commons-lang-DateUtils_1256/Dockerfile | 18 + Java/commons-lang-DateUtils_1256/buggy.java | 1875 ++++++++++++++++ .../commons-lang-DateUtils_1256/metadata.json | 21 + Java/commons-lang-DateUtils_1256/npe.json | 7 + Java/commons-lang-DateUtils_1658/Dockerfile | 18 + Java/commons-lang-DateUtils_1658/buggy.java | 1870 ++++++++++++++++ .../commons-lang-DateUtils_1658/metadata.json | 21 + Java/commons-lang-DateUtils_1658/npe.json | 7 + Java/commons-lang-DateUtils_1678/Dockerfile | 18 + Java/commons-lang-DateUtils_1678/buggy.java | 1868 ++++++++++++++++ .../commons-lang-DateUtils_1678/metadata.json | 21 + Java/commons-lang-DateUtils_1678/npe.json | 7 + Java/commons-lang-DateUtils_369/Dockerfile | 18 + Java/commons-lang-DateUtils_369/buggy.java | 1875 ++++++++++++++++ Java/commons-lang-DateUtils_369/metadata.json | 21 + Java/commons-lang-DateUtils_369/npe.json | 7 + Java/commons-lang-DateUtils_711/Dockerfile | 18 + Java/commons-lang-DateUtils_711/buggy.java | 1885 ++++++++++++++++ Java/commons-lang-DateUtils_711/metadata.json | 21 + Java/commons-lang-DateUtils_711/npe.json | 7 + Java/commons-lang-DateUtils_748/Dockerfile | 18 + Java/commons-lang-DateUtils_748/buggy.java | 1886 ++++++++++++++++ Java/commons-lang-DateUtils_748/metadata.json | 21 + Java/commons-lang-DateUtils_748/npe.json | 7 + Java/commons-lang-DateUtils_785/Dockerfile | 18 + Java/commons-lang-DateUtils_785/buggy.java | 1888 +++++++++++++++++ Java/commons-lang-DateUtils_785/metadata.json | 21 + Java/commons-lang-DateUtils_785/npe.json | 7 + Java/commons-lang-DateUtils_814/Dockerfile | 18 + Java/commons-lang-DateUtils_814/buggy.java | 1875 ++++++++++++++++ Java/commons-lang-DateUtils_814/metadata.json | 21 + Java/commons-lang-DateUtils_814/npe.json | 7 + Java/commons-lang-DateUtils_839/Dockerfile | 18 + Java/commons-lang-DateUtils_839/buggy.java | 1874 ++++++++++++++++ Java/commons-lang-DateUtils_839/metadata.json | 21 + Java/commons-lang-DateUtils_839/npe.json | 7 + Java/commons-lang-DateUtils_864/Dockerfile | 18 + Java/commons-lang-DateUtils_864/buggy.java | 1876 ++++++++++++++++ Java/commons-lang-DateUtils_864/metadata.json | 21 + Java/commons-lang-DateUtils_864/npe.json | 7 + Java/commons-lang-DateUtils_894/Dockerfile | 18 + Java/commons-lang-DateUtils_894/buggy.java | 1876 ++++++++++++++++ Java/commons-lang-DateUtils_894/metadata.json | 21 + Java/commons-lang-DateUtils_894/npe.json | 7 + Java/commons-lang-DateUtils_920/Dockerfile | 18 + Java/commons-lang-DateUtils_920/buggy.java | 1875 ++++++++++++++++ Java/commons-lang-DateUtils_920/metadata.json | 21 + Java/commons-lang-DateUtils_920/npe.json | 7 + Java/commons-lang-DateUtils_946/Dockerfile | 18 + Java/commons-lang-DateUtils_946/buggy.java | 1877 ++++++++++++++++ Java/commons-lang-DateUtils_946/metadata.json | 21 + Java/commons-lang-DateUtils_946/npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 171 ++ .../metadata.json | 21 + .../npe.json | 7 + Java/commons-lang-DiffBuilder_851/Dockerfile | 18 + Java/commons-lang-DiffBuilder_851/buggy.java | 1028 +++++++++ .../metadata.json | 21 + Java/commons-lang-DiffBuilder_851/npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 699 ++++++ .../metadata.json | 21 + .../npe.json | 7 + 68 files changed, 28964 insertions(+) create mode 100644 Java/commons-lang-DateUtils_1133/Dockerfile create mode 100644 Java/commons-lang-DateUtils_1133/buggy.java create mode 100644 Java/commons-lang-DateUtils_1133/metadata.json create mode 100644 Java/commons-lang-DateUtils_1133/npe.json create mode 100644 Java/commons-lang-DateUtils_1256/Dockerfile create mode 100644 Java/commons-lang-DateUtils_1256/buggy.java create mode 100644 Java/commons-lang-DateUtils_1256/metadata.json create mode 100644 Java/commons-lang-DateUtils_1256/npe.json create mode 100644 Java/commons-lang-DateUtils_1658/Dockerfile create mode 100644 Java/commons-lang-DateUtils_1658/buggy.java create mode 100644 Java/commons-lang-DateUtils_1658/metadata.json create mode 100644 Java/commons-lang-DateUtils_1658/npe.json create mode 100644 Java/commons-lang-DateUtils_1678/Dockerfile create mode 100644 Java/commons-lang-DateUtils_1678/buggy.java create mode 100644 Java/commons-lang-DateUtils_1678/metadata.json create mode 100644 Java/commons-lang-DateUtils_1678/npe.json create mode 100644 Java/commons-lang-DateUtils_369/Dockerfile create mode 100644 Java/commons-lang-DateUtils_369/buggy.java create mode 100644 Java/commons-lang-DateUtils_369/metadata.json create mode 100644 Java/commons-lang-DateUtils_369/npe.json create mode 100644 Java/commons-lang-DateUtils_711/Dockerfile create mode 100644 Java/commons-lang-DateUtils_711/buggy.java create mode 100644 Java/commons-lang-DateUtils_711/metadata.json create mode 100644 Java/commons-lang-DateUtils_711/npe.json create mode 100644 Java/commons-lang-DateUtils_748/Dockerfile create mode 100644 Java/commons-lang-DateUtils_748/buggy.java create mode 100644 Java/commons-lang-DateUtils_748/metadata.json create mode 100644 Java/commons-lang-DateUtils_748/npe.json create mode 100644 Java/commons-lang-DateUtils_785/Dockerfile create mode 100644 Java/commons-lang-DateUtils_785/buggy.java create mode 100644 Java/commons-lang-DateUtils_785/metadata.json create mode 100644 Java/commons-lang-DateUtils_785/npe.json create mode 100644 Java/commons-lang-DateUtils_814/Dockerfile create mode 100644 Java/commons-lang-DateUtils_814/buggy.java create mode 100644 Java/commons-lang-DateUtils_814/metadata.json create mode 100644 Java/commons-lang-DateUtils_814/npe.json create mode 100644 Java/commons-lang-DateUtils_839/Dockerfile create mode 100644 Java/commons-lang-DateUtils_839/buggy.java create mode 100644 Java/commons-lang-DateUtils_839/metadata.json create mode 100644 Java/commons-lang-DateUtils_839/npe.json create mode 100644 Java/commons-lang-DateUtils_864/Dockerfile create mode 100644 Java/commons-lang-DateUtils_864/buggy.java create mode 100644 Java/commons-lang-DateUtils_864/metadata.json create mode 100644 Java/commons-lang-DateUtils_864/npe.json create mode 100644 Java/commons-lang-DateUtils_894/Dockerfile create mode 100644 Java/commons-lang-DateUtils_894/buggy.java create mode 100644 Java/commons-lang-DateUtils_894/metadata.json create mode 100644 Java/commons-lang-DateUtils_894/npe.json create mode 100644 Java/commons-lang-DateUtils_920/Dockerfile create mode 100644 Java/commons-lang-DateUtils_920/buggy.java create mode 100644 Java/commons-lang-DateUtils_920/metadata.json create mode 100644 Java/commons-lang-DateUtils_920/npe.json create mode 100644 Java/commons-lang-DateUtils_946/Dockerfile create mode 100644 Java/commons-lang-DateUtils_946/buggy.java create mode 100644 Java/commons-lang-DateUtils_946/metadata.json create mode 100644 Java/commons-lang-DateUtils_946/npe.json create mode 100644 Java/commons-lang-DefaultExceptionContext_129/Dockerfile create mode 100644 Java/commons-lang-DefaultExceptionContext_129/buggy.java create mode 100644 Java/commons-lang-DefaultExceptionContext_129/metadata.json create mode 100644 Java/commons-lang-DefaultExceptionContext_129/npe.json create mode 100644 Java/commons-lang-DiffBuilder_851/Dockerfile create mode 100644 Java/commons-lang-DiffBuilder_851/buggy.java create mode 100644 Java/commons-lang-DiffBuilder_851/metadata.json create mode 100644 Java/commons-lang-DiffBuilder_851/npe.json create mode 100644 Java/commons-lang-DurationFormatUtils_552/Dockerfile create mode 100644 Java/commons-lang-DurationFormatUtils_552/buggy.java create mode 100644 Java/commons-lang-DurationFormatUtils_552/metadata.json create mode 100644 Java/commons-lang-DurationFormatUtils_552/npe.json diff --git a/Java/commons-lang-DateUtils_1133/Dockerfile b/Java/commons-lang-DateUtils_1133/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_1133/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_1133/buggy.java b/Java/commons-lang-DateUtils_1133/buggy.java new file mode 100644 index 000000000..77c836461 --- /dev/null +++ b/Java/commons-lang-DateUtils_1133/buggy.java @@ -0,0 +1,1884 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ * + * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ * + * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ * + * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ +// ----------------------------------------------------------------------- +/** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus + * the date to work with, not null + * @param rangeStyle + * the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException + * if the date is null + * @throws IllegalArgumentException + * if the rangeStyle is invalid + */ +public static java.util.Iterator iterator(final java.util.Date focus, final int rangeStyle) { + { + final java.util.Calendar gval = java.util.Calendar.getInstance(); + gval.setTime(/* NPEX_NULL_EXP */ + focus); + return org.apache.commons.lang3.time.DateUtils.iterator(gval, rangeStyle); + } +} + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + * + * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + * + * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + * + * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + * + * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + * + * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + * + * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + * + * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + * + * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + * + * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + * + * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_1133/metadata.json b/Java/commons-lang-DateUtils_1133/metadata.json new file mode 100644 index 000000000..a9e377230 --- /dev/null +++ b/Java/commons-lang-DateUtils_1133/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_1133", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 1165, + "npe_method": "iterator", + "deref_field": "focus", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_1133" + } +} diff --git a/Java/commons-lang-DateUtils_1133/npe.json b/Java/commons-lang-DateUtils_1133/npe.json new file mode 100644 index 000000000..a8f105bee --- /dev/null +++ b/Java/commons-lang-DateUtils_1133/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 1165, + "npe_method": "iterator", + "deref_field": "focus", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_1256/Dockerfile b/Java/commons-lang-DateUtils_1256/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_1256/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_1256/buggy.java b/Java/commons-lang-DateUtils_1256/buggy.java new file mode 100644 index 000000000..f62320c84 --- /dev/null +++ b/Java/commons-lang-DateUtils_1256/buggy.java @@ -0,0 +1,1875 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ +/** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus + * the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle + * the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ClassCastException + * if the object type is not a {@code Date} or {@code Calendar} + */ +public static java.util.Iterator iterator(final java.lang.Object focus, final int rangeStyle) { + { + if (/* NPEX_NULL_EXP */ + focus instanceof java.util.Date) { + return org.apache.commons.lang3.time.DateUtils.iterator(((java.util.Date) (focus)), rangeStyle); + } else if (focus instanceof java.util.Calendar) { + return org.apache.commons.lang3.time.DateUtils.iterator(((java.util.Calendar) (focus)), rangeStyle); + } else { + throw new java.lang.ClassCastException("Could not iterate based on " + focus); + } + } +} + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_1256/metadata.json b/Java/commons-lang-DateUtils_1256/metadata.json new file mode 100644 index 000000000..fe8793806 --- /dev/null +++ b/Java/commons-lang-DateUtils_1256/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_1256", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 1278, + "npe_method": "iterator", + "deref_field": "focus", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_1256" + } +} diff --git a/Java/commons-lang-DateUtils_1256/npe.json b/Java/commons-lang-DateUtils_1256/npe.json new file mode 100644 index 000000000..c7634dbea --- /dev/null +++ b/Java/commons-lang-DateUtils_1256/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 1278, + "npe_method": "iterator", + "deref_field": "focus", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_1658/Dockerfile b/Java/commons-lang-DateUtils_1658/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_1658/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_1658/buggy.java b/Java/commons-lang-DateUtils_1658/buggy.java new file mode 100644 index 000000000..859188699 --- /dev/null +++ b/Java/commons-lang-DateUtils_1658/buggy.java @@ -0,0 +1,1870 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ +/** + * Gets a Date fragment for any unit. + * + * @param date + * the date to work with, not null + * @param fragment + * the Calendar field part of date to calculate + * @param unit + * the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException + * if the date is null or + * fragment is not supported + * @since 2.4 + */ +private static long getFragment(final java.util.Date date, final int fragment, final java.util.concurrent.TimeUnit unit) { + { + final java.util.Calendar calendar = java.util.Calendar.getInstance(); + calendar.setTime(/* NPEX_NULL_EXP */ + date); + return org.apache.commons.lang3.time.DateUtils.getFragment(calendar, fragment, unit); + } +} + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_1658/metadata.json b/Java/commons-lang-DateUtils_1658/metadata.json new file mode 100644 index 000000000..d504b0229 --- /dev/null +++ b/Java/commons-lang-DateUtils_1658/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_1658", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 1676, + "npe_method": "getFragment", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_1658" + } +} diff --git a/Java/commons-lang-DateUtils_1658/npe.json b/Java/commons-lang-DateUtils_1658/npe.json new file mode 100644 index 000000000..15c2f817f --- /dev/null +++ b/Java/commons-lang-DateUtils_1658/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 1676, + "npe_method": "getFragment", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_1678/Dockerfile b/Java/commons-lang-DateUtils_1678/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_1678/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_1678/buggy.java b/Java/commons-lang-DateUtils_1678/buggy.java new file mode 100644 index 000000000..5d6c801d0 --- /dev/null +++ b/Java/commons-lang-DateUtils_1678/buggy.java @@ -0,0 +1,1868 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ +/** + * Gets a Calendar fragment for any unit. + * + * @param calendar + * the calendar to work with, not null + * @param fragment + * the Calendar field part of calendar to calculate + * @param unit + * the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException + * if the date is null or + * fragment is not supported + * @since 2.4 + */ +private static long getFragment(final java.util.Calendar calendar, final int fragment, final java.util.concurrent.TimeUnit unit) { + { + long result = 0; + final int offset = (unit == java.util.concurrent.TimeUnit.DAYS) ? 0 : 1; + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case java.util.Calendar.YEAR : + result += unit.convert(/* NPEX_NULL_EXP */ + calendar.get(java.util.Calendar.DAY_OF_YEAR) - offset, java.util.concurrent.TimeUnit.DAYS); + break; + case java.util.Calendar.MONTH : + result += unit.convert(calendar.get(java.util.Calendar.DAY_OF_MONTH) - offset, java.util.concurrent.TimeUnit.DAYS); + break; + default : + break; + } + switch (fragment) { + // Number of days already calculated for these cases + case java.util.Calendar.YEAR : + case java.util.Calendar.MONTH : + // The rest of the valid cases + case java.util.Calendar.DAY_OF_YEAR : + case java.util.Calendar.DATE : + result += unit.convert(calendar.get(java.util.Calendar.HOUR_OF_DAY), java.util.concurrent.TimeUnit.HOURS); + // $FALL-THROUGH$ + case java.util.Calendar.HOUR_OF_DAY : + result += unit.convert(calendar.get(java.util.Calendar.MINUTE), java.util.concurrent.TimeUnit.MINUTES); + // $FALL-THROUGH$ + case java.util.Calendar.MINUTE : + result += unit.convert(calendar.get(java.util.Calendar.SECOND), java.util.concurrent.TimeUnit.SECONDS); + // $FALL-THROUGH$ + case java.util.Calendar.SECOND : + result += unit.convert(calendar.get(java.util.Calendar.MILLISECOND), java.util.concurrent.TimeUnit.MILLISECONDS); + break; + case java.util.Calendar.MILLISECOND : + break;// never useful + + default : + throw new java.lang.IllegalArgumentException(("The fragment " + fragment) + " is not supported"); + } + return result; + } +} + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_1678/metadata.json b/Java/commons-lang-DateUtils_1678/metadata.json new file mode 100644 index 000000000..3e9d65041 --- /dev/null +++ b/Java/commons-lang-DateUtils_1678/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_1678", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 1700, + "npe_method": "getFragment", + "deref_field": "calendar", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_1678" + } +} diff --git a/Java/commons-lang-DateUtils_1678/npe.json b/Java/commons-lang-DateUtils_1678/npe.json new file mode 100644 index 000000000..2975241f1 --- /dev/null +++ b/Java/commons-lang-DateUtils_1678/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 1700, + "npe_method": "getFragment", + "deref_field": "calendar", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_369/Dockerfile b/Java/commons-lang-DateUtils_369/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_369/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_369/buggy.java b/Java/commons-lang-DateUtils_369/buggy.java new file mode 100644 index 000000000..0e1e9f0f0 --- /dev/null +++ b/Java/commons-lang-DateUtils_369/buggy.java @@ -0,0 +1,1875 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ +/** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str + * the date to parse, not null + * @param locale + * the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns + * the date format patterns to use, see SimpleDateFormat, not null + * @param lenient + * Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException + * if the date string or pattern array is null + * @throws ParseException + * if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ +private static java.util.Date parseDateWithLeniency(final java.lang.String str, final java.util.Locale locale, final java.lang.String[] parsePatterns, final boolean lenient) throws java.text.ParseException { + if ((str == null) || (parsePatterns == null)) { + throw new java.lang.IllegalArgumentException("Date and Patterns must not be null"); + } + final java.util.TimeZone tz = java.util.TimeZone.getDefault(); + final java.util.Locale lcl = /* NPEX_NULL_EXP */ + locale; + final java.text.ParsePosition pos = new java.text.ParsePosition(0); + final java.util.Calendar calendar = java.util.Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + for (final java.lang.String parsePattern : parsePatterns) { + org.apache.commons.lang3.time.FastDateParser fdp = new org.apache.commons.lang3.time.FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && (pos.getIndex() == str.length())) { + return calendar.getTime(); + } + } catch (java.lang.IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new java.text.ParseException("Unable to parse the date: " + str, -1); +} + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_369/metadata.json b/Java/commons-lang-DateUtils_369/metadata.json new file mode 100644 index 000000000..b28b61a96 --- /dev/null +++ b/Java/commons-lang-DateUtils_369/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_369", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 391, + "npe_method": "parseDateWithLeniency", + "deref_field": "locale", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_369" + } +} diff --git a/Java/commons-lang-DateUtils_369/npe.json b/Java/commons-lang-DateUtils_369/npe.json new file mode 100644 index 000000000..84bc18806 --- /dev/null +++ b/Java/commons-lang-DateUtils_369/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 391, + "npe_method": "parseDateWithLeniency", + "deref_field": "locale", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_711/Dockerfile b/Java/commons-lang-DateUtils_711/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_711/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_711/buggy.java b/Java/commons-lang-DateUtils_711/buggy.java new file mode 100644 index 000000000..8bbbcd057 --- /dev/null +++ b/Java/commons-lang-DateUtils_711/buggy.java @@ -0,0 +1,1885 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ +// ----------------------------------------------------------------------- +/** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date + * the date to work with, not null + * @param field + * the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException + * if the year is over 280 million + */ +public static java.util.Date round(final java.util.Date date, final int field) { + { + final java.util.Calendar gval = java.util.Calendar.getInstance(); + gval.setTime(/* NPEX_NULL_EXP */ + date); + org.apache.commons.lang3.time.DateUtils.modify(gval, field, org.apache.commons.lang3.time.DateUtils.ModifyType.ROUND); + return gval.getTime(); + } +} + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_711/metadata.json b/Java/commons-lang-DateUtils_711/metadata.json new file mode 100644 index 000000000..00aff0c38 --- /dev/null +++ b/Java/commons-lang-DateUtils_711/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_711", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 744, + "npe_method": "round", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_711" + } +} diff --git a/Java/commons-lang-DateUtils_711/npe.json b/Java/commons-lang-DateUtils_711/npe.json new file mode 100644 index 000000000..ed73f1bf4 --- /dev/null +++ b/Java/commons-lang-DateUtils_711/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 744, + "npe_method": "round", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_748/Dockerfile b/Java/commons-lang-DateUtils_748/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_748/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_748/buggy.java b/Java/commons-lang-DateUtils_748/buggy.java new file mode 100644 index 000000000..918e4742a --- /dev/null +++ b/Java/commons-lang-DateUtils_748/buggy.java @@ -0,0 +1,1886 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ +/** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date + * the date to work with, not null + * @param field + * the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ArithmeticException + * if the year is over 280 million + */ +public static java.util.Calendar round(final java.util.Calendar date, final int field) { + { + final java.util.Calendar rounded = ((java.util.Calendar) (/* NPEX_NULL_EXP */ + date.clone())); + org.apache.commons.lang3.time.DateUtils.modify(rounded, field, org.apache.commons.lang3.time.DateUtils.ModifyType.ROUND); + return rounded; + } +} + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_748/metadata.json b/Java/commons-lang-DateUtils_748/metadata.json new file mode 100644 index 000000000..1ea24f640 --- /dev/null +++ b/Java/commons-lang-DateUtils_748/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_748", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 781, + "npe_method": "round", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_748" + } +} diff --git a/Java/commons-lang-DateUtils_748/npe.json b/Java/commons-lang-DateUtils_748/npe.json new file mode 100644 index 000000000..de54a7200 --- /dev/null +++ b/Java/commons-lang-DateUtils_748/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 781, + "npe_method": "round", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_785/Dockerfile b/Java/commons-lang-DateUtils_785/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_785/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_785/buggy.java b/Java/commons-lang-DateUtils_785/buggy.java new file mode 100644 index 000000000..28dc71c95 --- /dev/null +++ b/Java/commons-lang-DateUtils_785/buggy.java @@ -0,0 +1,1888 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ +/** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date + * the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field + * the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ClassCastException + * if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException + * if the year is over 280 million + */ +public static java.util.Date round(final java.lang.Object date, final int field) { + { + if (/* NPEX_NULL_EXP */ + date instanceof java.util.Date) { + return org.apache.commons.lang3.time.DateUtils.round(((java.util.Date) (date)), field); + } else if (date instanceof java.util.Calendar) { + return org.apache.commons.lang3.time.DateUtils.round(((java.util.Calendar) (date)), field).getTime(); + } else { + throw new java.lang.ClassCastException("Could not round " + date); + } + } +} + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_785/metadata.json b/Java/commons-lang-DateUtils_785/metadata.json new file mode 100644 index 000000000..f70f984b2 --- /dev/null +++ b/Java/commons-lang-DateUtils_785/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_785", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 820, + "npe_method": "round", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_785" + } +} diff --git a/Java/commons-lang-DateUtils_785/npe.json b/Java/commons-lang-DateUtils_785/npe.json new file mode 100644 index 000000000..cbeb68650 --- /dev/null +++ b/Java/commons-lang-DateUtils_785/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 820, + "npe_method": "round", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_814/Dockerfile b/Java/commons-lang-DateUtils_814/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_814/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_814/buggy.java b/Java/commons-lang-DateUtils_814/buggy.java new file mode 100644 index 000000000..72fad20ce --- /dev/null +++ b/Java/commons-lang-DateUtils_814/buggy.java @@ -0,0 +1,1875 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ +// ----------------------------------------------------------------------- +/** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date + * the date to work with, not null + * @param field + * the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ArithmeticException + * if the year is over 280 million + */ +public static java.util.Date truncate(final java.util.Date date, final int field) { + { + final java.util.Calendar gval = java.util.Calendar.getInstance(); + gval.setTime(/* NPEX_NULL_EXP */ + date); + org.apache.commons.lang3.time.DateUtils.modify(gval, field, org.apache.commons.lang3.time.DateUtils.ModifyType.TRUNCATE); + return gval.getTime(); + } +} + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_814/metadata.json b/Java/commons-lang-DateUtils_814/metadata.json new file mode 100644 index 000000000..83e414e41 --- /dev/null +++ b/Java/commons-lang-DateUtils_814/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_814", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 837, + "npe_method": "truncate", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_814" + } +} diff --git a/Java/commons-lang-DateUtils_814/npe.json b/Java/commons-lang-DateUtils_814/npe.json new file mode 100644 index 000000000..d5677a476 --- /dev/null +++ b/Java/commons-lang-DateUtils_814/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 837, + "npe_method": "truncate", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_839/Dockerfile b/Java/commons-lang-DateUtils_839/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_839/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_839/buggy.java b/Java/commons-lang-DateUtils_839/buggy.java new file mode 100644 index 000000000..4b41b5d5e --- /dev/null +++ b/Java/commons-lang-DateUtils_839/buggy.java @@ -0,0 +1,1874 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ +/** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date + * the date to work with, not null + * @param field + * the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ArithmeticException + * if the year is over 280 million + */ +public static java.util.Calendar truncate(final java.util.Calendar date, final int field) { + { + final java.util.Calendar truncated = ((java.util.Calendar) (/* NPEX_NULL_EXP */ + date.clone())); + org.apache.commons.lang3.time.DateUtils.modify(truncated, field, org.apache.commons.lang3.time.DateUtils.ModifyType.TRUNCATE); + return truncated; + } +} + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_839/metadata.json b/Java/commons-lang-DateUtils_839/metadata.json new file mode 100644 index 000000000..75c9a49c6 --- /dev/null +++ b/Java/commons-lang-DateUtils_839/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_839", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 860, + "npe_method": "truncate", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_839" + } +} diff --git a/Java/commons-lang-DateUtils_839/npe.json b/Java/commons-lang-DateUtils_839/npe.json new file mode 100644 index 000000000..000cae16c --- /dev/null +++ b/Java/commons-lang-DateUtils_839/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 860, + "npe_method": "truncate", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_864/Dockerfile b/Java/commons-lang-DateUtils_864/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_864/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_864/buggy.java b/Java/commons-lang-DateUtils_864/buggy.java new file mode 100644 index 000000000..c668f4f38 --- /dev/null +++ b/Java/commons-lang-DateUtils_864/buggy.java @@ -0,0 +1,1876 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ +/** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date + * the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field + * the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ClassCastException + * if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException + * if the year is over 280 million + */ +public static java.util.Date truncate(final java.lang.Object date, final int field) { + { + if (/* NPEX_NULL_EXP */ + date instanceof java.util.Date) { + return org.apache.commons.lang3.time.DateUtils.truncate(((java.util.Date) (date)), field); + } else if (date instanceof java.util.Calendar) { + return org.apache.commons.lang3.time.DateUtils.truncate(((java.util.Calendar) (date)), field).getTime(); + } else { + throw new java.lang.ClassCastException("Could not truncate " + date); + } + } +} + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_864/metadata.json b/Java/commons-lang-DateUtils_864/metadata.json new file mode 100644 index 000000000..72ffca6a7 --- /dev/null +++ b/Java/commons-lang-DateUtils_864/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_864", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 887, + "npe_method": "truncate", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_864" + } +} diff --git a/Java/commons-lang-DateUtils_864/npe.json b/Java/commons-lang-DateUtils_864/npe.json new file mode 100644 index 000000000..a9d2e3ef4 --- /dev/null +++ b/Java/commons-lang-DateUtils_864/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 887, + "npe_method": "truncate", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_894/Dockerfile b/Java/commons-lang-DateUtils_894/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_894/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_894/buggy.java b/Java/commons-lang-DateUtils_894/buggy.java new file mode 100644 index 000000000..73d581e82 --- /dev/null +++ b/Java/commons-lang-DateUtils_894/buggy.java @@ -0,0 +1,1876 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ +// ----------------------------------------------------------------------- +/** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date + * the date to work with, not null + * @param field + * the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ArithmeticException + * if the year is over 280 million + * @since 2.5 + */ +public static java.util.Date ceiling(final java.util.Date date, final int field) { + { + final java.util.Calendar gval = java.util.Calendar.getInstance(); + gval.setTime(/* NPEX_NULL_EXP */ + date); + org.apache.commons.lang3.time.DateUtils.modify(gval, field, org.apache.commons.lang3.time.DateUtils.ModifyType.CEILING); + return gval.getTime(); + } +} + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_894/metadata.json b/Java/commons-lang-DateUtils_894/metadata.json new file mode 100644 index 000000000..f6aad8a79 --- /dev/null +++ b/Java/commons-lang-DateUtils_894/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_894", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 918, + "npe_method": "ceiling", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_894" + } +} diff --git a/Java/commons-lang-DateUtils_894/npe.json b/Java/commons-lang-DateUtils_894/npe.json new file mode 100644 index 000000000..c99d0e5c1 --- /dev/null +++ b/Java/commons-lang-DateUtils_894/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 918, + "npe_method": "ceiling", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_920/Dockerfile b/Java/commons-lang-DateUtils_920/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_920/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_920/buggy.java b/Java/commons-lang-DateUtils_920/buggy.java new file mode 100644 index 000000000..5f1c7f2a9 --- /dev/null +++ b/Java/commons-lang-DateUtils_920/buggy.java @@ -0,0 +1,1875 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ +/** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date + * the date to work with, not null + * @param field + * the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ArithmeticException + * if the year is over 280 million + * @since 2.5 + */ +public static java.util.Calendar ceiling(final java.util.Calendar date, final int field) { + { + final java.util.Calendar ceiled = ((java.util.Calendar) (/* NPEX_NULL_EXP */ + date.clone())); + org.apache.commons.lang3.time.DateUtils.modify(ceiled, field, org.apache.commons.lang3.time.DateUtils.ModifyType.CEILING); + return ceiled; + } +} + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return ceiling((Date) date, field); + } else if (date instanceof Calendar) { + return ceiling((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_920/metadata.json b/Java/commons-lang-DateUtils_920/metadata.json new file mode 100644 index 000000000..65900cb8b --- /dev/null +++ b/Java/commons-lang-DateUtils_920/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_920", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 942, + "npe_method": "ceiling", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_920" + } +} diff --git a/Java/commons-lang-DateUtils_920/npe.json b/Java/commons-lang-DateUtils_920/npe.json new file mode 100644 index 000000000..2b565f57c --- /dev/null +++ b/Java/commons-lang-DateUtils_920/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 942, + "npe_method": "ceiling", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DateUtils_946/Dockerfile b/Java/commons-lang-DateUtils_946/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DateUtils_946/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DateUtils_946/buggy.java b/Java/commons-lang-DateUtils_946/buggy.java new file mode 100644 index 000000000..0ed859a59 --- /dev/null +++ b/Java/commons-lang-DateUtils_946/buggy.java @@ -0,0 +1,1877 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + *

A suite of utilities surrounding the use of the + * {@link java.util.Calendar} and {@link java.util.Date} object.

+ * + *

DateUtils contains a lot of common methods considering manipulations + * of Dates or Calendars. Some methods require some extra explanation. + * The truncate, ceiling and round methods could be considered the Math.floor(), + * Math.ceil() or Math.round versions for dates + * This way date-fields will be ignored in bottom-up order. + * As a complement to these methods we've introduced some fragment-methods. + * With these methods the Date-fields will be ignored in top-down order. + * Since a date without a year is not a valid date, you have to decide in what + * kind of date-field you want your result, for instance milliseconds or days. + *

+ *

+ * Several methods are provided for adding to {@code Date} objects, of the form + * {@code addXXX(Date date, int amount)}. It is important to note these methods + * use a {@code Calendar} internally (with default timezone and locale) and may + * be affected by changes to daylight saving time (DST). + *

+ * + * @since 2.0 + */ +public class DateUtils { + + /** + * Number of milliseconds in a standard second. + * @since 2.1 + */ + public static final long MILLIS_PER_SECOND = 1000; + /** + * Number of milliseconds in a standard minute. + * @since 2.1 + */ + public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + /** + * Number of milliseconds in a standard hour. + * @since 2.1 + */ + public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + /** + * Number of milliseconds in a standard day. + * @since 2.1 + */ + public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; + + /** + * This is half a month, so this represents whether a date is in the top + * or bottom half of the month. + */ + public static final int SEMI_MONTH = 1001; + + private static final int[][] fields = { + {Calendar.MILLISECOND}, + {Calendar.SECOND}, + {Calendar.MINUTE}, + {Calendar.HOUR_OF_DAY, Calendar.HOUR}, + {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM + /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ + }, + {Calendar.MONTH, DateUtils.SEMI_MONTH}, + {Calendar.YEAR}, + {Calendar.ERA}}; + + /** + * A week range, starting on Sunday. + */ + public static final int RANGE_WEEK_SUNDAY = 1; + /** + * A week range, starting on Monday. + */ + public static final int RANGE_WEEK_MONDAY = 2; + /** + * A week range, starting on the day focused. + */ + public static final int RANGE_WEEK_RELATIVE = 3; + /** + * A week range, centered around the day focused. + */ + public static final int RANGE_WEEK_CENTER = 4; + /** + * A month range, the week starting on Sunday. + */ + public static final int RANGE_MONTH_SUNDAY = 5; + /** + * A month range, the week starting on Monday. + */ + public static final int RANGE_MONTH_MONDAY = 6; + + /** + * Calendar modification types. + */ + private enum ModifyType { + /** + * Truncation. + */ + TRUNCATE, + + /** + * Rounding. + */ + ROUND, + + /** + * Ceiling. + */ + CEILING + } + + /** + *

{@code DateUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code DateUtils.parseDate(str);}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public DateUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameDay(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar cal1 = Calendar.getInstance(); + cal1.setTime(date1); + final Calendar cal2 = Calendar.getInstance(); + cal2.setTime(date2); + return isSameDay(cal1, cal2); + } + + /** + *

Checks if two calendar objects are on the same day ignoring time.

+ * + *

28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. + * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. + *

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same day + * @throws IllegalArgumentException if either calendar is null + * @since 2.1 + */ + public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two date objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param date1 the first date, not altered, not null + * @param date2 the second date, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Date date1, final Date date2) { + if (date1 == null || date2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return date1.getTime() == date2.getTime(); + } + + /** + *

Checks if two calendar objects represent the same instant in time.

+ * + *

This method compares the long millisecond time of the two objects.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.getTime().getTime() == cal2.getTime().getTime(); + } + + //----------------------------------------------------------------------- + /** + *

Checks if two calendar objects represent the same local time.

+ * + *

This method compares the values of the fields of the two objects. + * In addition, both calendars must be the same of the same type.

+ * + * @param cal1 the first calendar, not altered, not null + * @param cal2 the second calendar, not altered, not null + * @return true if they represent the same millisecond instant + * @throws IllegalArgumentException if either date is null + * @since 2.1 + */ + public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { + if (cal1 == null || cal2 == null) { + throw new IllegalArgumentException("The date must not be null"); + } + return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.getClass() == cal2.getClass(); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + */ + public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { + return parseDate(str, null, parsePatterns); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser will be lenient toward the parsed date. + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDate(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable (or there were none) + * @since 3.2 + */ + public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, true); + } + + //----------------------------------------------------------------------- + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 2.5 + */ + public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { + return parseDateStrictly(str, null, parsePatterns); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers, + * using the default date format symbols for the given locale..

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * The parser parses strictly - it does not allow for dates such as "February 942, 1996". + * + * @param str the date to parse, not null + * @param locale the locale whose date format symbols should be used. If null, + * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @since 3.2 + */ + public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { + return parseDateWithLeniency(str, locale, parsePatterns, false); + } + + /** + *

Parses a string representing a date by trying a variety of different parsers.

+ * + *

The parse will try each parse pattern in turn. + * A parse is only deemed successful if it parses the whole of the input string. + * If no parse patterns match, a ParseException is thrown.

+ * + * @param str the date to parse, not null + * @param locale the locale to use when interpretting the pattern, can be null in which + * case the default system locale is used + * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null + * @param lenient Specify whether or not date/time parsing is to be lenient. + * @return the parsed date + * @throws IllegalArgumentException if the date string or pattern array is null + * @throws ParseException if none of the date patterns were suitable + * @see java.util.Calendar#isLenient() + */ + private static Date parseDateWithLeniency( + final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { + if (str == null || parsePatterns == null) { + throw new IllegalArgumentException("Date and Patterns must not be null"); + } + + final TimeZone tz = TimeZone.getDefault(); + final Locale lcl = locale==null ?Locale.getDefault() : locale; + final ParsePosition pos = new ParsePosition(0); + final Calendar calendar = Calendar.getInstance(tz, lcl); + calendar.setLenient(lenient); + + for (final String parsePattern : parsePatterns) { + FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); + calendar.clear(); + try { + if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { + return calendar.getTime(); + } + } + catch(IllegalArgumentException ignore) { + // leniency is preventing calendar from being set + } + pos.setIndex(0); + } + throw new ParseException("Unable to parse the date: " + str, -1); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of years to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addYears(final Date date, final int amount) { + return add(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of months to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMonths(final Date date, final int amount) { + return add(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of weeks to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addWeeks(final Date date, final int amount) { + return add(date, Calendar.WEEK_OF_YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of days to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addDays(final Date date, final int amount) { + return add(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of hours to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addHours(final Date date, final int amount) { + return add(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of minutes to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMinutes(final Date date, final int amount) { + return add(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of seconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addSeconds(final Date date, final int amount) { + return add(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds a number of milliseconds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + public static Date addMilliseconds(final Date date, final int amount) { + return add(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Adds to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the calendar field to add to + * @param amount the amount to add, may be negative + * @return the new {@code Date} with the amount added + * @throws IllegalArgumentException if the date is null + */ + private static Date add(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Sets the years field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setYears(final Date date, final int amount) { + return set(date, Calendar.YEAR, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the months field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMonths(final Date date, final int amount) { + return set(date, Calendar.MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the day of month field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setDays(final Date date, final int amount) { + return set(date, Calendar.DAY_OF_MONTH, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the hours field to a date returning a new object. Hours range + * from 0-23. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setHours(final Date date, final int amount) { + return set(date, Calendar.HOUR_OF_DAY, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the minute field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMinutes(final Date date, final int amount) { + return set(date, Calendar.MINUTE, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the seconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setSeconds(final Date date, final int amount) { + return set(date, Calendar.SECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the milliseconds field to a date returning a new object. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + public static Date setMilliseconds(final Date date, final int amount) { + return set(date, Calendar.MILLISECOND, amount); + } + + //----------------------------------------------------------------------- + /** + * Sets the specified field to a date returning a new object. + * This does not use a lenient calendar. + * The original {@code Date} is unchanged. + * + * @param date the date, not null + * @param calendarField the {@code Calendar} field to set the amount to + * @param amount the amount to set + * @return a new {@code Date} set with the specified value + * @throws IllegalArgumentException if the date is null + * @since 2.4 + */ + private static Date set(final Date date, final int calendarField, final int amount) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + // getInstance() returns a new object, so this method is thread safe. + final Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTime(date); + c.set(calendarField, amount); + return c.getTime(); + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} into a {@code Calendar}. + * + * @param date the date to convert to a Calendar + * @return the created Calendar + * @throws NullPointerException if null is passed in + * @since 3.0 + */ + public static Calendar toCalendar(final Date date) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} + * @param date the date to convert to a Calendar + * @param tz the time zone of the @{code date} + * @return the created Calendar + * @throws NullPointerException if {@code date} or {@code tz} is null + */ + public static Calendar toCalendar(final Date date, final TimeZone tz) { + final Calendar c = Calendar.getInstance(tz); + c.setTime(date); + return c; + } + + //----------------------------------------------------------------------- + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or {@code SEMI_MONTH} + * @return the different rounded date, not null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.ROUND); + return gval.getTime(); + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar round(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar rounded = (Calendar) date.clone(); + modify(rounded, field, ModifyType.ROUND); + return rounded; + } + + /** + *

Rounds a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if this was passed with HOUR, it would return + * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it + * would return 1 April 2002 0:00:00.000.

+ * + *

For a date in a timezone that handles the change to daylight + * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. + * Suppose daylight saving time begins at 02:00 on March 30. Rounding a + * date that crosses this time would produce the following values: + *

+ *
    + *
  • March 30, 2003 01:10 rounds to March 30, 2003 01:00
  • + *
  • March 30, 2003 01:40 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:10 rounds to March 30, 2003 03:00
  • + *
  • March 30, 2003 02:40 rounds to March 30, 2003 04:00
  • + *
+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different rounded date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date round(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return round((Date) date, field); + } else if (date instanceof Calendar) { + return round((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not round " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.TRUNCATE); + return gval.getTime(); + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + */ + public static Calendar truncate(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar truncated = (Calendar) date.clone(); + modify(truncated, field, ModifyType.TRUNCATE); + return truncated; + } + + /** + *

Truncates a date, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 13:00:00.000. If this was passed with MONTH, it would + * return 1 Mar 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different truncated date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + */ + public static Date truncate(final Object date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (date instanceof Date) { + return truncate((Date) date, field); + } else if (date instanceof Calendar) { + return truncate((Calendar) date, field).getTime(); + } else { + throw new ClassCastException("Could not truncate " + date); + } + } + + //----------------------------------------------------------------------- + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Date ceiling(final Date date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(date); + modify(gval, field, ModifyType.CEILING); + return gval.getTime(); + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ + public static Calendar ceiling(final Calendar date, final int field) { + if (date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar ceiled = (Calendar) date.clone(); + modify(ceiled, field, ModifyType.CEILING); + return ceiled; + } + + /** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException if the year is over 280 million + * @since 2.5 + */ +/** + *

Gets a date ceiling, leaving the field specified as the most + * significant field.

+ * + *

For example, if you had the date-time of 28 Mar 2002 + * 13:45:01.231, if you passed with HOUR, it would return 28 Mar + * 2002 14:00:00.000. If this was passed with MONTH, it would + * return 1 Apr 2002 0:00:00.000.

+ * + * @param date + * the date to work with, either {@code Date} or {@code Calendar}, not null + * @param field + * the field from {@code Calendar} or SEMI_MONTH + * @return the different ceil date, not null + * @throws IllegalArgumentException + * if the date is null + * @throws ClassCastException + * if the object type is not a {@code Date} or {@code Calendar} + * @throws ArithmeticException + * if the year is over 280 million + * @since 2.5 + */ +public static java.util.Date ceiling(final java.lang.Object date, final int field) { + { + if (/* NPEX_NULL_EXP */ + date instanceof java.util.Date) { + return org.apache.commons.lang3.time.DateUtils.ceiling(((java.util.Date) (date)), field); + } else if (date instanceof java.util.Calendar) { + return org.apache.commons.lang3.time.DateUtils.ceiling(((java.util.Calendar) (date)), field).getTime(); + } else { + throw new java.lang.ClassCastException("Could not find ceiling of for type: " + date.getClass()); + } + } +} + + //----------------------------------------------------------------------- + /** + *

Internal calculation method.

+ * + * @param val the calendar, not null + * @param field the field constant + * @param modType type to truncate, round or ceiling + * @throws ArithmeticException if the year is over 280 million + */ + private static void modify(final Calendar val, final int field, final ModifyType modType) { + if (val.get(Calendar.YEAR) > 280000000) { + throw new ArithmeticException("Calendar value too large for accurate calculations"); + } + + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + final Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + final int millisecs = val.get(Calendar.MILLISECOND); + if (ModifyType.TRUNCATE == modType || millisecs < 500) { + time = time - millisecs; + } + if (field == Calendar.SECOND) { + done = true; + } + + // truncate seconds + final int seconds = val.get(Calendar.SECOND); + if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { + time = time - (seconds * 1000L); + } + if (field == Calendar.MINUTE) { + done = true; + } + + // truncate minutes + final int minutes = val.get(Calendar.MINUTE); + if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + + boolean roundUp = false; + for (final int[] aField : fields) { + for (final int element : aField) { + if (element == field) { + //This is our field... we stop looping + if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { + if (field == DateUtils.SEMI_MONTH) { + //This is a special case that's hard to generalize + //If the date is 1, we round up to 16, otherwise + // we subtract 15 days and add 1 month + if (val.get(Calendar.DATE) == 1) { + val.add(Calendar.DATE, 15); + } else { + val.add(Calendar.DATE, -15); + val.add(Calendar.MONTH, 1); + } +// ----------------- Fix for LANG-440 ---------------------- START --------------- + } else if (field == Calendar.AM_PM) { + // This is a special case + // If the time is 0, we round up to 12, otherwise + // we subtract 12 hours and add 1 day + if (val.get(Calendar.HOUR_OF_DAY) == 0) { + val.add(Calendar.HOUR_OF_DAY, 12); + } else { + val.add(Calendar.HOUR_OF_DAY, -12); + val.add(Calendar.DATE, 1); + } +// ----------------- Fix for LANG-440 ---------------------- END --------------- + } else { + //We need at add one to this field since the + // last number causes us to round up + val.add(aField[0], 1); + } + } + return; + } + } + //We have various fields that are not easy roundings + int offset = 0; + boolean offsetSet = false; + //These are special types of fields that require different rounding rules + switch (field) { + case DateUtils.SEMI_MONTH: + if (aField[0] == Calendar.DATE) { + //If we're going to drop the DATE field's value, + // we want to do this our own way. + //We need to subtrace 1 since the date has a minimum of 1 + offset = val.get(Calendar.DATE) - 1; + //If we're above 15 days adjustment, that means we're in the + // bottom half of the month and should stay accordingly. + if (offset >= 15) { + offset -= 15; + } + //Record whether we're in the top or bottom half of that range + roundUp = offset > 7; + offsetSet = true; + } + break; + case Calendar.AM_PM: + if (aField[0] == Calendar.HOUR_OF_DAY) { + //If we're going to drop the HOUR field's value, + // we want to do this our own way. + offset = val.get(Calendar.HOUR_OF_DAY); + if (offset >= 12) { + offset -= 12; + } + roundUp = offset >= 6; + offsetSet = true; + } + break; + default: + break; + } + if (!offsetSet) { + final int min = val.getActualMinimum(aField[0]); + final int max = val.getActualMaximum(aField[0]); + //Calculate the offset from the minimum allowed value + offset = val.get(aField[0]) - min; + //Set roundUp if this is more than half way between the minimum and maximum + roundUp = offset > ((max - min) / 2); + } + //We need to remove this field + if (offset != 0) { + val.set(aField[0], val.get(aField[0]) - offset); + } + } + throw new IllegalArgumentException("The field " + field + " is not supported"); + + } + + //----------------------------------------------------------------------- + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Date focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar gval = Calendar.getInstance(); + gval.setTime(focus); + return iterator(gval, rangeStyle); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + *

This method provides an iterator that returns Calendar objects. + * The days are progressed using {@link Calendar#add(int, int)}.

+ * + * @param focus the date to work with, not null + * @param rangeStyle the style constant to use. Must be one of + * {@link DateUtils#RANGE_MONTH_SUNDAY}, + * {@link DateUtils#RANGE_MONTH_MONDAY}, + * {@link DateUtils#RANGE_WEEK_SUNDAY}, + * {@link DateUtils#RANGE_WEEK_MONDAY}, + * {@link DateUtils#RANGE_WEEK_RELATIVE}, + * {@link DateUtils#RANGE_WEEK_CENTER} + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws IllegalArgumentException if the rangeStyle is invalid + */ + public static Iterator iterator(final Calendar focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + Calendar start = null; + Calendar end = null; + int startCutoff = Calendar.SUNDAY; + int endCutoff = Calendar.SATURDAY; + switch (rangeStyle) { + case RANGE_MONTH_SUNDAY: + case RANGE_MONTH_MONDAY: + //Set start to the first of the month + start = truncate(focus, Calendar.MONTH); + //Set end to the last of the month + end = (Calendar) start.clone(); + end.add(Calendar.MONTH, 1); + end.add(Calendar.DATE, -1); + //Loop start back to the previous sunday or monday + if (rangeStyle == RANGE_MONTH_MONDAY) { + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + } + break; + case RANGE_WEEK_SUNDAY: + case RANGE_WEEK_MONDAY: + case RANGE_WEEK_RELATIVE: + case RANGE_WEEK_CENTER: + //Set start and end to the current date + start = truncate(focus, Calendar.DATE); + end = truncate(focus, Calendar.DATE); + switch (rangeStyle) { + case RANGE_WEEK_SUNDAY: + //already set by default + break; + case RANGE_WEEK_MONDAY: + startCutoff = Calendar.MONDAY; + endCutoff = Calendar.SUNDAY; + break; + case RANGE_WEEK_RELATIVE: + startCutoff = focus.get(Calendar.DAY_OF_WEEK); + endCutoff = startCutoff - 1; + break; + case RANGE_WEEK_CENTER: + startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; + endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; + break; + default: + break; + } + break; + default: + throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); + } + if (startCutoff < Calendar.SUNDAY) { + startCutoff += 7; + } + if (startCutoff > Calendar.SATURDAY) { + startCutoff -= 7; + } + if (endCutoff < Calendar.SUNDAY) { + endCutoff += 7; + } + if (endCutoff > Calendar.SATURDAY) { + endCutoff -= 7; + } + while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { + start.add(Calendar.DATE, -1); + } + while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { + end.add(Calendar.DATE, 1); + } + return new DateIterator(start, end); + } + + /** + *

Constructs an Iterator over each day in a date + * range defined by a focus date and range style.

+ * + *

For instance, passing Thursday, July 4, 2002 and a + * RANGE_MONTH_SUNDAY will return an Iterator + * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, + * 2002, returning a Calendar instance for each intermediate day.

+ * + * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null + * @param rangeStyle the style constant to use. Must be one of the range + * styles listed for the {@link #iterator(Calendar, int)} method. + * @return the date iterator, not null + * @throws IllegalArgumentException if the date is null + * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} + */ + public static Iterator iterator(final Object focus, final int rangeStyle) { + if (focus == null) { + throw new IllegalArgumentException("The date must not be null"); + } + if (focus instanceof Date) { + return iterator((Date) focus, rangeStyle); + } else if (focus instanceof Calendar) { + return iterator((Calendar) focus, rangeStyle); + } else { + throw new ClassCastException("Could not iterate based on " + focus); + } + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of milliseconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all milliseconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MILLISECONDS); + } + + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to deprecated date.getSeconds())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to deprecated date.getMinutes())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to deprecated date.getHours())
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to deprecated date.getDay())
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param date the date to work with, not null + * @param fragment the {@code Calendar} field part of date to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Date date, final int fragment) { + return getFragment(date, fragment, TimeUnit.DAYS); + } + + /** + *

Returns the number of milliseconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the milliseconds of any date will only return the number of milliseconds + * of the current second (resulting in a number between 0 and 999). This + * method will retrieve the number of milliseconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s), minutes(s) and second(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MILLISECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 + * (equivalent to calendar.get(Calendar.MILLISECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 + * (10*1000 + 538)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in milliseconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of milliseconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); + } + /** + *

Returns the number of seconds within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the seconds of any date will only return the number of seconds + * of the current minute (resulting in a number between 0 and 59). This + * method will retrieve the number of seconds for any fragment. + * For example, if you want to calculate the number of seconds past today, + * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will + * be all seconds of the past hour(s) and minutes(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a SECOND field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 + * (equivalent to calendar.get(Calendar.SECOND))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 + * (7*3600 + 15*60 + 10)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in seconds)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of seconds within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.SECONDS); + } + + /** + *

Returns the number of minutes within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the minutes of any date will only return the number of minutes + * of the current hour (resulting in a number between 0 and 59). This + * method will retrieve the number of minutes for any fragment. + * For example, if you want to calculate the number of minutes past this month, + * your fragment is Calendar.MONTH. The result will be all minutes of the + * past day(s) and hour(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a MINUTE field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 + * (equivalent to calendar.get(Calendar.MINUTES))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in minutes)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of minutes within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.MINUTES); + } + + /** + *

Returns the number of hours within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the hours of any date will only return the number of hours + * of the current day (resulting in a number between 0 and 23). This + * method will retrieve the number of hours for any fragment. + * For example, if you want to calculate the number of hours past this month, + * your fragment is Calendar.MONTH. The result will be all hours of the + * past day(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a HOUR field will return 0.

+ * + *
    + *
  • January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 + * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))
  • + *
  • January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7
  • + *
  • January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)
  • + *
  • January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in hours)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of hours within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInHours(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.HOURS); + } + + /** + *

Returns the number of days within the + * fragment. All datefields greater than the fragment will be ignored.

+ * + *

Asking the days of any date will only return the number of days + * of the current month (resulting in a number between 1 and 31). This + * method will retrieve the number of days for any fragment. + * For example, if you want to calculate the number of days past this year, + * your fragment is Calendar.YEAR. The result will be all days of the + * past month(s).

+ * + *

Valid fragments are: Calendar.YEAR, Calendar.MONTH, both + * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, + * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND + * A fragment less than or equal to a DAY field will return 0.

+ * + *
    + *
  • January 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • February 28, 2008 with Calendar.MONTH as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))
  • + *
  • January 28, 2008 with Calendar.YEAR as fragment will return 28 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • February 28, 2008 with Calendar.YEAR as fragment will return 59 + * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))
  • + *
  • January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 + * (a millisecond cannot be split in days)
  • + *
+ * + * @param calendar the calendar to work with, not null + * @param fragment the {@code Calendar} field part of calendar to calculate + * @return number of days within the fragment of date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + public static long getFragmentInDays(final Calendar calendar, final int fragment) { + return getFragment(calendar, fragment, TimeUnit.DAYS); + } + + /** + * Gets a Date fragment for any unit. + * + * @param date the date to work with, not null + * @param fragment the Calendar field part of date to calculate + * @param unit the time unit + * @return number of units within the fragment of the date + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { + if(date == null) { + throw new IllegalArgumentException("The date must not be null"); + } + final Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return getFragment(calendar, fragment, unit); + } + + /** + * Gets a Calendar fragment for any unit. + * + * @param calendar the calendar to work with, not null + * @param fragment the Calendar field part of calendar to calculate + * @param unit the time unit + * @return number of units within the fragment of the calendar + * @throws IllegalArgumentException if the date is null or + * fragment is not supported + * @since 2.4 + */ + private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { + if(calendar == null) { + throw new IllegalArgumentException("The date must not be null"); + } + + long result = 0; + + final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; + + // Fragments bigger than a day require a breakdown to days + switch (fragment) { + case Calendar.YEAR: + result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); + break; + case Calendar.MONTH: + result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); + break; + default: + break; + } + + switch (fragment) { + // Number of days already calculated for these cases + case Calendar.YEAR: + case Calendar.MONTH: + + // The rest of the valid cases + case Calendar.DAY_OF_YEAR: + case Calendar.DATE: + result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); + //$FALL-THROUGH$ + case Calendar.HOUR_OF_DAY: + result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); + //$FALL-THROUGH$ + case Calendar.MINUTE: + result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); + //$FALL-THROUGH$ + case Calendar.SECOND: + result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); + break; + case Calendar.MILLISECOND: break;//never useful + default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); + } + return result; + } + + /** + * Determines if two calendars are equal up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedEquals(Date, Date, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { + return truncatedCompareTo(cal1, cal2, field) == 0; + } + + /** + * Determines if two dates are equal up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from {@code Calendar} + * @return true if equal; otherwise false + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Date, int) + * @see #truncatedEquals(Calendar, Calendar, int) + * @since 3.0 + */ + public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { + return truncatedCompareTo(date1, date2, field) == 0; + } + + /** + * Determines how two calendars compare up to no more than the specified + * most significant field. + * + * @param cal1 the first calendar, not null + * @param cal2 the second calendar, not null + * @param field the field from {@code Calendar} + * @return a negative integer, zero, or a positive integer as the first + * calendar is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { + final Calendar truncatedCal1 = truncate(cal1, field); + final Calendar truncatedCal2 = truncate(cal2, field); + return truncatedCal1.compareTo(truncatedCal2); + } + + /** + * Determines how two dates compare up to no more than the specified + * most significant field. + * + * @param date1 the first date, not null + * @param date2 the second date, not null + * @param field the field from Calendar + * @return a negative integer, zero, or a positive integer as the first + * date is less than, equal to, or greater than the second. + * @throws IllegalArgumentException if any argument is null + * @see #truncate(Calendar, int) + * @see #truncatedCompareTo(Date, Date, int) + * @since 3.0 + */ + public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { + final Date truncatedDate1 = truncate(date1, field); + final Date truncatedDate2 = truncate(date2, field); + return truncatedDate1.compareTo(truncatedDate2); + } + + + //----------------------------------------------------------------------- + /** + *

Date iterator.

+ */ + static class DateIterator implements Iterator { + private final Calendar endFinal; + private final Calendar spot; + + /** + * Constructs a DateIterator that ranges from one date to another. + * + * @param startFinal start date (inclusive) + * @param endFinal end date (inclusive) + */ + DateIterator(final Calendar startFinal, final Calendar endFinal) { + super(); + this.endFinal = endFinal; + spot = startFinal; + spot.add(Calendar.DATE, -1); + } + + /** + * Has the iterator not reached the end date yet? + * + * @return true if the iterator has yet to reach the end date + */ + @Override + public boolean hasNext() { + return spot.before(endFinal); + } + + /** + * Return the next calendar in the iteration + * + * @return Object calendar for the next date + */ + @Override + public Calendar next() { + if (spot.equals(endFinal)) { + throw new NoSuchElementException(); + } + spot.add(Calendar.DATE, 1); + return (Calendar) spot.clone(); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Java/commons-lang-DateUtils_946/metadata.json b/Java/commons-lang-DateUtils_946/metadata.json new file mode 100644 index 000000000..25069ed11 --- /dev/null +++ b/Java/commons-lang-DateUtils_946/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DateUtils_946", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 970, + "npe_method": "ceiling", + "deref_field": "date", + "npe_class": "DateUtils", + "repo": "commons-lang", + "bug_id": "DateUtils_946" + } +} diff --git a/Java/commons-lang-DateUtils_946/npe.json b/Java/commons-lang-DateUtils_946/npe.json new file mode 100644 index 000000000..654d98355 --- /dev/null +++ b/Java/commons-lang-DateUtils_946/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DateUtils.java", + "line": 970, + "npe_method": "ceiling", + "deref_field": "date", + "npe_class": "DateUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-DefaultExceptionContext_129/Dockerfile b/Java/commons-lang-DefaultExceptionContext_129/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DefaultExceptionContext_129/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DefaultExceptionContext_129/buggy.java b/Java/commons-lang-DefaultExceptionContext_129/buggy.java new file mode 100644 index 000000000..dea4c445b --- /dev/null +++ b/Java/commons-lang-DefaultExceptionContext_129/buggy.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Default implementation of the context storing the label-value pairs for contexted exceptions. + *

+ * This implementation is serializable, however this is dependent on the values that + * are added also being serializable. + *

+ * + * @see ContextedException + * @see ContextedRuntimeException + * @since 3.0 + */ +public class DefaultExceptionContext implements ExceptionContext, Serializable { + + /** The serialization version. */ + private static final long serialVersionUID = 20110706L; + + /** The list storing the label-data pairs. */ + private final List> contextValues = new ArrayList>(); + + /** + * {@inheritDoc} + */ + @Override + public DefaultExceptionContext addContextValue(final String label, final Object value) { + contextValues.add(new ImmutablePair(label, value)); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public DefaultExceptionContext setContextValue(final String label, final Object value) { + for (final Iterator> iter = contextValues.iterator(); iter.hasNext();) { + final Pair p = iter.next(); + if (StringUtils.equals(label, p.getKey())) { + iter.remove(); + } + } + addContextValue(label, value); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public List getContextValues(final String label) { + final List values = new ArrayList(); + for (final Pair pair : contextValues) { + if (StringUtils.equals(label, pair.getKey())) { + values.add(pair.getValue()); + } + } + return values; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getFirstContextValue(final String label) { + for (final Pair pair : contextValues) { + if (StringUtils.equals(label, pair.getKey())) { + return pair.getValue(); + } + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Set getContextLabels() { + final Set labels = new HashSet(); + for (final Pair pair : contextValues) { + labels.add(pair.getKey()); + } + return labels; + } + + /** + * {@inheritDoc} + */ + @Override + public List> getContextEntries() { + return contextValues; + } + + /** + * Builds the message containing the contextual information. + * + * @param baseMessage the base exception message without context information appended + * @return the exception message with context information appended, never null + */ + @Override +/** + * Builds the message containing the contextual information. + * + * @param baseMessage + * the base exception message without context information appended + * @return the exception message with context information appended, never null + */ +public java.lang.String getFormattedExceptionMessage(final java.lang.String baseMessage) { + final java.lang.StringBuilder buffer = new java.lang.StringBuilder(256); + { + buffer.append(/* NPEX_NULL_EXP */ + baseMessage); + } + if (contextValues.size() > 0) { + if (buffer.length() > 0) { + buffer.append('\n'); + } + buffer.append("Exception Context:\n"); + int i = 0; + for (final org.apache.commons.lang3.tuple.Pair pair : contextValues) { + buffer.append("\t["); + buffer.append(++i); + buffer.append(':'); + buffer.append(pair.getKey()); + buffer.append("="); + final java.lang.Object value = pair.getValue(); + if (value == null) { + buffer.append("null"); + } else { + java.lang.String valueStr; + try { + valueStr = value.toString(); + } catch (final java.lang.Exception e) { + valueStr = "Exception thrown on toString(): " + org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e); + } + buffer.append(valueStr); + } + buffer.append("]\n"); + } + buffer.append("---------------------------------"); + } + return buffer.toString(); +} + +} diff --git a/Java/commons-lang-DefaultExceptionContext_129/metadata.json b/Java/commons-lang-DefaultExceptionContext_129/metadata.json new file mode 100644 index 000000000..34d1cf32c --- /dev/null +++ b/Java/commons-lang-DefaultExceptionContext_129/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DefaultExceptionContext_129", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/DefaultExceptionContext.java", + "line": 138, + "npe_method": "getFormattedExceptionMessage", + "deref_field": "baseMessage", + "npe_class": "DefaultExceptionContext", + "repo": "commons-lang", + "bug_id": "DefaultExceptionContext_129" + } +} diff --git a/Java/commons-lang-DefaultExceptionContext_129/npe.json b/Java/commons-lang-DefaultExceptionContext_129/npe.json new file mode 100644 index 000000000..9137c7923 --- /dev/null +++ b/Java/commons-lang-DefaultExceptionContext_129/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/DefaultExceptionContext.java", + "line": 138, + "npe_method": "getFormattedExceptionMessage", + "deref_field": "baseMessage", + "npe_class": "DefaultExceptionContext" +} \ No newline at end of file diff --git a/Java/commons-lang-DiffBuilder_851/Dockerfile b/Java/commons-lang-DiffBuilder_851/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DiffBuilder_851/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DiffBuilder_851/buggy.java b/Java/commons-lang-DiffBuilder_851/buggy.java new file mode 100644 index 000000000..078c01ac5 --- /dev/null +++ b/Java/commons-lang-DiffBuilder_851/buggy.java @@ -0,0 +1,1028 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; + +/** + *

+ * Assists in implementing {@link Diffable#diff(Object)} methods. + *

+ * + *

+ * To use this class, write code as follows: + *

+ * + *
+ * public class Person implements Diffable<Person> {
+ *   String name;
+ *   int age;
+ *   boolean smoker;
+ *   
+ *   ...
+ *   
+ *   public DiffResult diff(Person obj) {
+ *     // No need for null check, as NullPointerException correct if obj is null
+ *     return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
+ *       .append("name", this.name, obj.name)
+ *       .append("age", this.age, obj.age)
+ *       .append("smoker", this.smoker, obj.smoker)
+ *       .build();
+ *   }
+ * }
+ * 
+ * + *

+ * The {@code ToStringStyle} passed to the constructor is embedded in the + * returned {@code DiffResult} and influences the style of the + * {@code DiffResult.toString()} method. This style choice can be overridden by + * calling {@link DiffResult#toString(ToStringStyle)}. + *

+ * + * @since 3.3 + * @see Diffable + * @see Diff + * @see DiffResult + * @see ToStringStyle + */ +public class DiffBuilder implements Builder { + + private final List> diffs; + private final boolean objectsTriviallyEqual; + private final Object left; + private final Object right; + private final ToStringStyle style; + + /** + *

+ * Constructs a builder for the specified objects with the specified style. + *

+ * + *

+ * If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will + * not evaluate any calls to {@code append(...)} and will return an empty + * {@link DiffResult} when {@link #build()} is executed. + *

+ * + * @param lhs + * {@code this} object + * @param rhs + * the object to diff against + * @param style + * the style will use when outputting the objects, {@code null} + * uses the default + * @param testTriviallyEqual + * If true, this will test if lhs and rhs are the same or equal. + * All of the append(fieldName, lhs, rhs) methods will abort + * without creating a field {@link Diff} if the trivially equal + * test is enabled and returns true. The result of this test + * is never changed throughout the life of this {@link DiffBuilder}. + * @throws IllegalArgumentException + * if {@code lhs} or {@code rhs} is {@code null} + * @since 3.4 + */ + public DiffBuilder(final Object lhs, final Object rhs, + final ToStringStyle style, final boolean testTriviallyEqual) { + + if (lhs == null) { + throw new IllegalArgumentException("lhs cannot be null"); + } + if (rhs == null) { + throw new IllegalArgumentException("rhs cannot be null"); + } + + this.diffs = new ArrayList>(); + this.left = lhs; + this.right = rhs; + this.style = style; + + // Don't compare any fields if objects equal + this.objectsTriviallyEqual = testTriviallyEqual && (lhs == rhs || lhs.equals(rhs)); + } + + /** + *

+ * Constructs a builder for the specified objects with the specified style. + *

+ * + *

+ * If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will + * not evaluate any calls to {@code append(...)} and will return an empty + * {@link DiffResult} when {@link #build()} is executed. + *

+ * + *

+ * This delegates to {@link #DiffBuilder(Object, Object, ToStringStyle, boolean)} + * with the testTriviallyEqual flag enabled. + *

+ * + * @param lhs + * {@code this} object + * @param rhs + * the object to diff against + * @param style + * the style will use when outputting the objects, {@code null} + * uses the default + * @throws IllegalArgumentException + * if {@code lhs} or {@code rhs} is {@code null} + */ + public DiffBuilder(final Object lhs, final Object rhs, + final ToStringStyle style) { + + this(lhs, rhs, style, true); + } + + /** + *

+ * Test if two {@code boolean}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code boolean} + * @param rhs + * the right hand {@code boolean} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final boolean lhs, + final boolean rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (lhs != rhs) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Boolean getLeft() { + return Boolean.valueOf(lhs); + } + + @Override + public Boolean getRight() { + return Boolean.valueOf(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code boolean[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code boolean[]} + * @param rhs + * the right hand {@code boolean[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final boolean[] lhs, + final boolean[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + if (objectsTriviallyEqual) { + return this; + } + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Boolean[] getLeft() { + return ArrayUtils.toObject(lhs); + } + + @Override + public Boolean[] getRight() { + return ArrayUtils.toObject(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code byte}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code byte} + * @param rhs + * the right hand {@code byte} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final byte lhs, + final byte rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + if (objectsTriviallyEqual) { + return this; + } + if (lhs != rhs) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Byte getLeft() { + return Byte.valueOf(lhs); + } + + @Override + public Byte getRight() { + return Byte.valueOf(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code byte[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code byte[]} + * @param rhs + * the right hand {@code byte[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final byte[] lhs, + final byte[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Byte[] getLeft() { + return ArrayUtils.toObject(lhs); + } + + @Override + public Byte[] getRight() { + return ArrayUtils.toObject(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code char}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code char} + * @param rhs + * the right hand {@code char} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final char lhs, + final char rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (lhs != rhs) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Character getLeft() { + return Character.valueOf(lhs); + } + + @Override + public Character getRight() { + return Character.valueOf(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code char[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code char[]} + * @param rhs + * the right hand {@code char[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final char[] lhs, + final char[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Character[] getLeft() { + return ArrayUtils.toObject(lhs); + } + + @Override + public Character[] getRight() { + return ArrayUtils.toObject(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code double}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code double} + * @param rhs + * the right hand {@code double} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final double lhs, + final double rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (Double.doubleToLongBits(lhs) != Double.doubleToLongBits(rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Double getLeft() { + return Double.valueOf(lhs); + } + + @Override + public Double getRight() { + return Double.valueOf(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code double[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code double[]} + * @param rhs + * the right hand {@code double[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final double[] lhs, + final double[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Double[] getLeft() { + return ArrayUtils.toObject(lhs); + } + + @Override + public Double[] getRight() { + return ArrayUtils.toObject(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code float}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code float} + * @param rhs + * the right hand {@code float} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final float lhs, + final float rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (Float.floatToIntBits(lhs) != Float.floatToIntBits(rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Float getLeft() { + return Float.valueOf(lhs); + } + + @Override + public Float getRight() { + return Float.valueOf(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code float[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code float[]} + * @param rhs + * the right hand {@code float[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final float[] lhs, + final float[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Float[] getLeft() { + return ArrayUtils.toObject(lhs); + } + + @Override + public Float[] getRight() { + return ArrayUtils.toObject(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code int}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code int} + * @param rhs + * the right hand {@code int} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final int lhs, + final int rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (lhs != rhs) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Integer getLeft() { + return Integer.valueOf(lhs); + } + + @Override + public Integer getRight() { + return Integer.valueOf(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code int[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code int[]} + * @param rhs + * the right hand {@code int[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final int[] lhs, + final int[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Integer[] getLeft() { + return ArrayUtils.toObject(lhs); + } + + @Override + public Integer[] getRight() { + return ArrayUtils.toObject(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code long}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code long} + * @param rhs + * the right hand {@code long} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final long lhs, + final long rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (lhs != rhs) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Long getLeft() { + return Long.valueOf(lhs); + } + + @Override + public Long getRight() { + return Long.valueOf(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code long[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code long[]} + * @param rhs + * the right hand {@code long[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final long[] lhs, + final long[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Long[] getLeft() { + return ArrayUtils.toObject(lhs); + } + + @Override + public Long[] getRight() { + return ArrayUtils.toObject(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code short}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code short} + * @param rhs + * the right hand {@code short} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final short lhs, + final short rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (lhs != rhs) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Short getLeft() { + return Short.valueOf(lhs); + } + + @Override + public Short getRight() { + return Short.valueOf(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code short[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code short[]} + * @param rhs + * the right hand {@code short[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final short[] lhs, + final short[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + + if (objectsTriviallyEqual) { + return this; + } + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Short[] getLeft() { + return ArrayUtils.toObject(lhs); + } + + @Override + public Short[] getRight() { + return ArrayUtils.toObject(rhs); + } + }); + } + return this; + } + + /** + *

+ * Test if two {@code Objects}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code Object} + * @param rhs + * the right hand {@code Object} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ +/** + *

+ * Test if two {@code Objects}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code Object} + * @param rhs + * the right hand {@code Object} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ +public org.apache.commons.lang3.builder.DiffBuilder append(final java.lang.String fieldName, final java.lang.Object lhs, final java.lang.Object rhs) { + if (fieldName == null) { + throw new java.lang.IllegalArgumentException("Field name cannot be null"); + } + if (objectsTriviallyEqual) { + return this; + } + if (lhs == rhs) { + return this; + } + java.lang.Object objectToTest; + { + objectToTest = /* NPEX_NULL_EXP */ + lhs; + } + if (objectToTest.getClass().isArray()) { + if (objectToTest instanceof boolean[]) { + return append(fieldName, ((boolean[]) (lhs)), ((boolean[]) (rhs))); + } + if (objectToTest instanceof byte[]) { + return append(fieldName, ((byte[]) (lhs)), ((byte[]) (rhs))); + } + if (objectToTest instanceof char[]) { + return append(fieldName, ((char[]) (lhs)), ((char[]) (rhs))); + } + if (objectToTest instanceof double[]) { + return append(fieldName, ((double[]) (lhs)), ((double[]) (rhs))); + } + if (objectToTest instanceof float[]) { + return append(fieldName, ((float[]) (lhs)), ((float[]) (rhs))); + } + if (objectToTest instanceof int[]) { + return append(fieldName, ((int[]) (lhs)), ((int[]) (rhs))); + } + if (objectToTest instanceof long[]) { + return append(fieldName, ((long[]) (lhs)), ((long[]) (rhs))); + } + if (objectToTest instanceof short[]) { + return append(fieldName, ((short[]) (lhs)), ((short[]) (rhs))); + } + return append(fieldName, ((java.lang.Object[]) (lhs)), ((java.lang.Object[]) (rhs))); + } + // Not array type + if ((lhs != null) && lhs.equals(rhs)) { + return this; + } + diffs.add(new org.apache.commons.lang3.builder.Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @java.lang.Override + public java.lang.Object getLeft() { + return lhs; + } + + @java.lang.Override + public java.lang.Object getRight() { + return rhs; + } + }); + return this; +} + + /** + *

+ * Test if two {@code Object[]}s are equal. + *

+ * + * @param fieldName + * the field name + * @param lhs + * the left hand {@code Object[]} + * @param rhs + * the right hand {@code Object[]} + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + */ + public DiffBuilder append(final String fieldName, final Object[] lhs, + final Object[] rhs) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + if (objectsTriviallyEqual) { + return this; + } + + if (!Arrays.equals(lhs, rhs)) { + diffs.add(new Diff(fieldName) { + private static final long serialVersionUID = 1L; + + @Override + public Object[] getLeft() { + return lhs; + } + + @Override + public Object[] getRight() { + return rhs; + } + }); + } + + return this; + } + + /** + *

+ * Append diffs from another {@code DiffResult}. + *

+ * + *

+ * This method is useful if you want to compare properties which are + * themselves Diffable and would like to know which specific part of + * it is different. + *

+ * + *
+     * public class Person implements Diffable<Person> {
+     *   String name;
+     *   Address address; // implements Diffable<Address>
+     *   
+     *   ...
+     *   
+     *   public DiffResult diff(Person obj) {
+     *     return new DiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
+     *       .append("name", this.name, obj.name)
+     *       .append("address", this.address.diff(obj.address))
+     *       .build();
+     *   }
+     * }
+     * 
+ * + * @param fieldName + * the field name + * @param diffResult + * the {@code DiffResult} to append + * @return this + * @throws IllegalArgumentException + * if field name is {@code null} + * @since 3.5 + */ + public DiffBuilder append(final String fieldName, + final DiffResult diffResult) { + if (fieldName == null) { + throw new IllegalArgumentException("Field name cannot be null"); + } + if (diffResult == null) { + throw new IllegalArgumentException("Diff result cannot be null"); + } + if (objectsTriviallyEqual) { + return this; + } + + for (Diff diff : diffResult.getDiffs()) { + append(fieldName + "." + diff.getFieldName(), + diff.getLeft(), diff.getRight()); + } + + return this; + } + + /** + *

+ * Builds a {@link DiffResult} based on the differences appended to this + * builder. + *

+ * + * @return a {@code DiffResult} containing the differences between the two + * objects. + */ + @Override + public DiffResult build() { + return new DiffResult(left, right, diffs, style); + } + +} diff --git a/Java/commons-lang-DiffBuilder_851/metadata.json b/Java/commons-lang-DiffBuilder_851/metadata.json new file mode 100644 index 000000000..066c58f2f --- /dev/null +++ b/Java/commons-lang-DiffBuilder_851/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DiffBuilder_851", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/builder/DiffBuilder.java", + "line": 866, + "npe_method": "append", + "deref_field": "lhs", + "npe_class": "DiffBuilder", + "repo": "commons-lang", + "bug_id": "DiffBuilder_851" + } +} diff --git a/Java/commons-lang-DiffBuilder_851/npe.json b/Java/commons-lang-DiffBuilder_851/npe.json new file mode 100644 index 000000000..8e495b6cf --- /dev/null +++ b/Java/commons-lang-DiffBuilder_851/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/builder/DiffBuilder.java", + "line": 866, + "npe_method": "append", + "deref_field": "lhs", + "npe_class": "DiffBuilder" +} \ No newline at end of file diff --git a/Java/commons-lang-DurationFormatUtils_552/Dockerfile b/Java/commons-lang-DurationFormatUtils_552/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-DurationFormatUtils_552/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-DurationFormatUtils_552/buggy.java b/Java/commons-lang-DurationFormatUtils_552/buggy.java new file mode 100644 index 000000000..3909a9d65 --- /dev/null +++ b/Java/commons-lang-DurationFormatUtils_552/buggy.java @@ -0,0 +1,699 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.time; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + +/** + *

Duration formatting utilities and constants. The following table describes the tokens + * used in the pattern language for formatting.

+ * + * + * + * + * + * + * + * + * + * + *
characterduration element
yyears
Mmonths
ddays
Hhours
mminutes
sseconds
Smilliseconds
'text'arbitrary text content
+ * + * Note: It's not currently possible to include a single-quote in a format. + *
+ * Token values are printed using decimal digits. + * A token character can be repeated to ensure that the field occupies a certain minimum + * size. Values will be left-padded with 0 unless padding is disabled in the method invocation. + * @since 2.1 + */ +public class DurationFormatUtils { + + /** + *

DurationFormatUtils instances should NOT be constructed in standard programming.

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ */ + public DurationFormatUtils() { + super(); + } + + /** + *

Pattern used with FastDateFormat and SimpleDateFormat + * for the ISO 8601 period format used in durations.

+ * + * @see org.apache.commons.lang3.time.FastDateFormat + * @see java.text.SimpleDateFormat + */ + public static final String ISO_EXTENDED_FORMAT_PATTERN = "'P'yyyy'Y'M'M'd'DT'H'H'm'M's.SSS'S'"; + + //----------------------------------------------------------------------- + /** + *

Formats the time gap as a string.

+ * + *

The format used is ISO 8601-like: {@code HH:mm:ss.SSS}.

+ * + * @param durationMillis the duration to format + * @return the formatted duration, not null + * @throws java.lang.IllegalArgumentException if durationMillis is negative + */ + public static String formatDurationHMS(final long durationMillis) { + return formatDuration(durationMillis, "HH:mm:ss.SSS"); + } + + /** + *

Formats the time gap as a string.

+ * + *

The format used is the ISO 8601 period format.

+ * + *

This method formats durations using the days and lower fields of the + * ISO format pattern, such as P7D6TH5M4.321S.

+ * + * @param durationMillis the duration to format + * @return the formatted duration, not null + * @throws java.lang.IllegalArgumentException if durationMillis is negative + */ + public static String formatDurationISO(final long durationMillis) { + return formatDuration(durationMillis, ISO_EXTENDED_FORMAT_PATTERN, false); + } + + /** + *

Formats the time gap as a string, using the specified format, and padding with zeros.

+ * + *

This method formats durations using the days and lower fields of the + * format pattern. Months and larger are not used.

+ * + * @param durationMillis the duration to format + * @param format the way in which to format the duration, not null + * @return the formatted duration, not null + * @throws java.lang.IllegalArgumentException if durationMillis is negative + */ + public static String formatDuration(final long durationMillis, final String format) { + return formatDuration(durationMillis, format, true); + } + + /** + *

Formats the time gap as a string, using the specified format. + * Padding the left hand side of numbers with zeroes is optional.

+ * + *

This method formats durations using the days and lower fields of the + * format pattern. Months and larger are not used.

+ * + * @param durationMillis the duration to format + * @param format the way in which to format the duration, not null + * @param padWithZeros whether to pad the left hand side of numbers with 0's + * @return the formatted duration, not null + * @throws java.lang.IllegalArgumentException if durationMillis is negative + */ + public static String formatDuration(final long durationMillis, final String format, final boolean padWithZeros) { + Validate.inclusiveBetween(0, Long.MAX_VALUE, durationMillis, "durationMillis must not be negative"); + + final Token[] tokens = lexx(format); + + long days = 0; + long hours = 0; + long minutes = 0; + long seconds = 0; + long milliseconds = durationMillis; + + if (Token.containsTokenWithValue(tokens, d) ) { + days = milliseconds / DateUtils.MILLIS_PER_DAY; + milliseconds = milliseconds - (days * DateUtils.MILLIS_PER_DAY); + } + if (Token.containsTokenWithValue(tokens, H) ) { + hours = milliseconds / DateUtils.MILLIS_PER_HOUR; + milliseconds = milliseconds - (hours * DateUtils.MILLIS_PER_HOUR); + } + if (Token.containsTokenWithValue(tokens, m) ) { + minutes = milliseconds / DateUtils.MILLIS_PER_MINUTE; + milliseconds = milliseconds - (minutes * DateUtils.MILLIS_PER_MINUTE); + } + if (Token.containsTokenWithValue(tokens, s) ) { + seconds = milliseconds / DateUtils.MILLIS_PER_SECOND; + milliseconds = milliseconds - (seconds * DateUtils.MILLIS_PER_SECOND); + } + + return format(tokens, 0, 0, days, hours, minutes, seconds, milliseconds, padWithZeros); + } + + /** + *

Formats an elapsed time into a pluralization correct string.

+ * + *

This method formats durations using the days and lower fields of the + * format pattern. Months and larger are not used.

+ * + * @param durationMillis the elapsed time to report in milliseconds + * @param suppressLeadingZeroElements suppresses leading 0 elements + * @param suppressTrailingZeroElements suppresses trailing 0 elements + * @return the formatted text in days/hours/minutes/seconds, not null + * @throws java.lang.IllegalArgumentException if durationMillis is negative + */ + public static String formatDurationWords( + final long durationMillis, + final boolean suppressLeadingZeroElements, + final boolean suppressTrailingZeroElements) { + + // This method is generally replaceable by the format method, but + // there are a series of tweaks and special cases that require + // trickery to replicate. + String duration = formatDuration(durationMillis, "d' days 'H' hours 'm' minutes 's' seconds'"); + if (suppressLeadingZeroElements) { + // this is a temporary marker on the front. Like ^ in regexp. + duration = " " + duration; + String tmp = StringUtils.replaceOnce(duration, " 0 days", StringUtils.EMPTY); + if (tmp.length() != duration.length()) { + duration = tmp; + tmp = StringUtils.replaceOnce(duration, " 0 hours", StringUtils.EMPTY); + if (tmp.length() != duration.length()) { + duration = tmp; + tmp = StringUtils.replaceOnce(duration, " 0 minutes", StringUtils.EMPTY); + duration = tmp; + if (tmp.length() != duration.length()) { + duration = StringUtils.replaceOnce(tmp, " 0 seconds", StringUtils.EMPTY); + } + } + } + if (duration.length() != 0) { + // strip the space off again + duration = duration.substring(1); + } + } + if (suppressTrailingZeroElements) { + String tmp = StringUtils.replaceOnce(duration, " 0 seconds", StringUtils.EMPTY); + if (tmp.length() != duration.length()) { + duration = tmp; + tmp = StringUtils.replaceOnce(duration, " 0 minutes", StringUtils.EMPTY); + if (tmp.length() != duration.length()) { + duration = tmp; + tmp = StringUtils.replaceOnce(duration, " 0 hours", StringUtils.EMPTY); + if (tmp.length() != duration.length()) { + duration = StringUtils.replaceOnce(tmp, " 0 days", StringUtils.EMPTY); + } + } + } + } + // handle plurals + duration = " " + duration; + duration = StringUtils.replaceOnce(duration, " 1 seconds", " 1 second"); + duration = StringUtils.replaceOnce(duration, " 1 minutes", " 1 minute"); + duration = StringUtils.replaceOnce(duration, " 1 hours", " 1 hour"); + duration = StringUtils.replaceOnce(duration, " 1 days", " 1 day"); + return duration.trim(); + } + + //----------------------------------------------------------------------- + /** + *

Formats the time gap as a string.

+ * + *

The format used is the ISO 8601 period format.

+ * + * @param startMillis the start of the duration to format + * @param endMillis the end of the duration to format + * @return the formatted duration, not null + * @throws java.lang.IllegalArgumentException if startMillis is greater than endMillis + */ + public static String formatPeriodISO(final long startMillis, final long endMillis) { + return formatPeriod(startMillis, endMillis, ISO_EXTENDED_FORMAT_PATTERN, false, TimeZone.getDefault()); + } + + /** + *

Formats the time gap as a string, using the specified format. + * Padding the left hand side of numbers with zeroes is optional. + * + * @param startMillis the start of the duration + * @param endMillis the end of the duration + * @param format the way in which to format the duration, not null + * @return the formatted duration, not null + * @throws java.lang.IllegalArgumentException if startMillis is greater than endMillis + */ + public static String formatPeriod(final long startMillis, final long endMillis, final String format) { + return formatPeriod(startMillis, endMillis, format, true, TimeZone.getDefault()); + } + + /** + *

Formats the time gap as a string, using the specified format. + * Padding the left hand side of numbers with zeroes is optional and + * the timezone may be specified.

+ * + *

When calculating the difference between months/days, it chooses to + * calculate months first. So when working out the number of months and + * days between January 15th and March 10th, it choose 1 month and + * 23 days gained by choosing January->February = 1 month and then + * calculating days forwards, and not the 1 month and 26 days gained by + * choosing March -> February = 1 month and then calculating days + * backwards.

+ * + *

For more control, the Joda-Time + * library is recommended.

+ * + * @param startMillis the start of the duration + * @param endMillis the end of the duration + * @param format the way in which to format the duration, not null + * @param padWithZeros whether to pad the left hand side of numbers with 0's + * @param timezone the millis are defined in + * @return the formatted duration, not null + * @throws java.lang.IllegalArgumentException if startMillis is greater than endMillis + */ + public static String formatPeriod(final long startMillis, final long endMillis, final String format, final boolean padWithZeros, + final TimeZone timezone) { + Validate.isTrue(startMillis <= endMillis, "startMillis must not be greater than endMillis"); + + + // Used to optimise for differences under 28 days and + // called formatDuration(millis, format); however this did not work + // over leap years. + // TODO: Compare performance to see if anything was lost by + // losing this optimisation. + + final Token[] tokens = lexx(format); + + // timezones get funky around 0, so normalizing everything to GMT + // stops the hours being off + final Calendar start = Calendar.getInstance(timezone); + start.setTime(new Date(startMillis)); + final Calendar end = Calendar.getInstance(timezone); + end.setTime(new Date(endMillis)); + + // initial estimates + int milliseconds = end.get(Calendar.MILLISECOND) - start.get(Calendar.MILLISECOND); + int seconds = end.get(Calendar.SECOND) - start.get(Calendar.SECOND); + int minutes = end.get(Calendar.MINUTE) - start.get(Calendar.MINUTE); + int hours = end.get(Calendar.HOUR_OF_DAY) - start.get(Calendar.HOUR_OF_DAY); + int days = end.get(Calendar.DAY_OF_MONTH) - start.get(Calendar.DAY_OF_MONTH); + int months = end.get(Calendar.MONTH) - start.get(Calendar.MONTH); + int years = end.get(Calendar.YEAR) - start.get(Calendar.YEAR); + + // each initial estimate is adjusted in case it is under 0 + while (milliseconds < 0) { + milliseconds += 1000; + seconds -= 1; + } + while (seconds < 0) { + seconds += 60; + minutes -= 1; + } + while (minutes < 0) { + minutes += 60; + hours -= 1; + } + while (hours < 0) { + hours += 24; + days -= 1; + } + + if (Token.containsTokenWithValue(tokens, M)) { + while (days < 0) { + days += start.getActualMaximum(Calendar.DAY_OF_MONTH); + months -= 1; + start.add(Calendar.MONTH, 1); + } + + while (months < 0) { + months += 12; + years -= 1; + } + + if (!Token.containsTokenWithValue(tokens, y) && years != 0) { + while (years != 0) { + months += 12 * years; + years = 0; + } + } + } else { + // there are no M's in the format string + + if( !Token.containsTokenWithValue(tokens, y) ) { + int target = end.get(Calendar.YEAR); + if (months < 0) { + // target is end-year -1 + target -= 1; + } + + while (start.get(Calendar.YEAR) != target) { + days += start.getActualMaximum(Calendar.DAY_OF_YEAR) - start.get(Calendar.DAY_OF_YEAR); + + // Not sure I grok why this is needed, but the brutal tests show it is + if (start instanceof GregorianCalendar && + start.get(Calendar.MONTH) == Calendar.FEBRUARY && + start.get(Calendar.DAY_OF_MONTH) == 29) { + days += 1; + } + + start.add(Calendar.YEAR, 1); + + days += start.get(Calendar.DAY_OF_YEAR); + } + + years = 0; + } + + while( start.get(Calendar.MONTH) != end.get(Calendar.MONTH) ) { + days += start.getActualMaximum(Calendar.DAY_OF_MONTH); + start.add(Calendar.MONTH, 1); + } + + months = 0; + + while (days < 0) { + days += start.getActualMaximum(Calendar.DAY_OF_MONTH); + months -= 1; + start.add(Calendar.MONTH, 1); + } + + } + + // The rest of this code adds in values that + // aren't requested. This allows the user to ask for the + // number of months and get the real count and not just 0->11. + + if (!Token.containsTokenWithValue(tokens, d)) { + hours += 24 * days; + days = 0; + } + if (!Token.containsTokenWithValue(tokens, H)) { + minutes += 60 * hours; + hours = 0; + } + if (!Token.containsTokenWithValue(tokens, m)) { + seconds += 60 * minutes; + minutes = 0; + } + if (!Token.containsTokenWithValue(tokens, s)) { + milliseconds += 1000 * seconds; + seconds = 0; + } + + return format(tokens, years, months, days, hours, minutes, seconds, milliseconds, padWithZeros); + } + + //----------------------------------------------------------------------- + /** + *

The internal method to do the formatting.

+ * + * @param tokens the tokens + * @param years the number of years + * @param months the number of months + * @param days the number of days + * @param hours the number of hours + * @param minutes the number of minutes + * @param seconds the number of seconds + * @param milliseconds the number of millis + * @param padWithZeros whether to pad + * @return the formatted string + */ + static String format(final Token[] tokens, final long years, final long months, final long days, final long hours, final long minutes, final long seconds, + final long milliseconds, final boolean padWithZeros) { + final StringBuilder buffer = new StringBuilder(); + boolean lastOutputSeconds = false; + for (final Token token : tokens) { + final Object value = token.getValue(); + final int count = token.getCount(); + if (value instanceof StringBuilder) { + buffer.append(value.toString()); + } else { + if (value.equals(y)) { + buffer.append(paddedValue(years, padWithZeros, count)); + lastOutputSeconds = false; + } else if (value.equals(M)) { + buffer.append(paddedValue(months, padWithZeros, count)); + lastOutputSeconds = false; + } else if (value.equals(d)) { + buffer.append(paddedValue(days, padWithZeros, count)); + lastOutputSeconds = false; + } else if (value.equals(H)) { + buffer.append(paddedValue(hours, padWithZeros, count)); + lastOutputSeconds = false; + } else if (value.equals(m)) { + buffer.append(paddedValue(minutes, padWithZeros, count)); + lastOutputSeconds = false; + } else if (value.equals(s)) { + buffer.append(paddedValue(seconds, padWithZeros, count)); + lastOutputSeconds = true; + } else if (value.equals(S)) { + if (lastOutputSeconds) { + // ensure at least 3 digits are displayed even if padding is not selected + final int width = padWithZeros ? Math.max(3, count) : 3; + buffer.append(paddedValue(milliseconds, true, width)); + } else { + buffer.append(paddedValue(milliseconds, padWithZeros, count)); + } + lastOutputSeconds = false; + } + } + } + return buffer.toString(); + } + + /** + *

Converts a {@code long} to a {@code String} with optional + * zero padding.

+ * + * @param value the value to convert + * @param padWithZeros whether to pad with zeroes + * @param count the size to pad to (ignored if {@code padWithZeros} is false) + * @return the string result + */ + private static String paddedValue(final long value, final boolean padWithZeros, final int count) { + final String longString = Long.toString(value); + return padWithZeros ? StringUtils.leftPad(longString, count, '0') : longString; + } + + static final Object y = "y"; + static final Object M = "M"; + static final Object d = "d"; + static final Object H = "H"; + static final Object m = "m"; + static final Object s = "s"; + static final Object S = "S"; + + /** + * Parses a classic date format string into Tokens + * + * @param format the format to parse, not null + * @return array of Token[] + */ +/** + * Parses a classic date format string into Tokens + * + * @param format + * the format to parse, not null + * @return array of Token[] + */ +static org.apache.commons.lang3.time.DurationFormatUtils.Token[] lexx(final java.lang.String format) { + final java.util.ArrayList list = new java.util.ArrayList(format.length()); + boolean inLiteral = false; + // Although the buffer is stored in a Token, the Tokens are only + // used internally, so cannot be accessed by other threads + java.lang.StringBuilder buffer = null; + org.apache.commons.lang3.time.DurationFormatUtils.Token previous = null; + for (int i = 0; i < format.length(); i++) { + final char ch = format.charAt(i); + if (inLiteral && (ch != '\'')) { + buffer.append(ch);// buffer can't be null if inLiteral is true + + continue; + } + java.lang.Object value = null; + switch (ch) { + // TODO: Need to handle escaping of ' + case '\'' : + if (inLiteral) { + buffer = null; + inLiteral = false; + } else { + buffer = new java.lang.StringBuilder(); + list.add(new org.apache.commons.lang3.time.DurationFormatUtils.Token(buffer)); + inLiteral = true; + } + break; + case 'y' : + value = org.apache.commons.lang3.time.DurationFormatUtils.y; + break; + case 'M' : + value = org.apache.commons.lang3.time.DurationFormatUtils.M; + break; + case 'd' : + value = org.apache.commons.lang3.time.DurationFormatUtils.d; + break; + case 'H' : + value = org.apache.commons.lang3.time.DurationFormatUtils.H; + break; + case 'm' : + value = org.apache.commons.lang3.time.DurationFormatUtils.m; + break; + case 's' : + value = org.apache.commons.lang3.time.DurationFormatUtils.s; + break; + case 'S' : + value = org.apache.commons.lang3.time.DurationFormatUtils.S; + break; + default : + if (buffer == null) { + buffer = new java.lang.StringBuilder(); + list.add(new org.apache.commons.lang3.time.DurationFormatUtils.Token(buffer)); + } + buffer.append(ch); + } + { + if ((previous != null) && previous.getValue().equals(/* NPEX_NULL_EXP */ + value)) { + previous.increment(); + } else { + final org.apache.commons.lang3.time.DurationFormatUtils.Token token = new org.apache.commons.lang3.time.DurationFormatUtils.Token(value); + list.add(token); + previous = token; + } + buffer = null; + } + } + if (inLiteral) { + // i.e. we have not found the end of the literal + throw new java.lang.IllegalArgumentException("Unmatched quote in format: " + format); + } + return list.toArray(new org.apache.commons.lang3.time.DurationFormatUtils.Token[list.size()]); +} + + //----------------------------------------------------------------------- + /** + * Element that is parsed from the format pattern. + */ + static class Token { + + /** + * Helper method to determine if a set of tokens contain a value + * + * @param tokens set to look in + * @param value to look for + * @return boolean true if contained + */ + static boolean containsTokenWithValue(final Token[] tokens, final Object value) { + for (final Token token : tokens) { + if (token.getValue() == value) { + return true; + } + } + return false; + } + + private final Object value; + private int count; + + /** + * Wraps a token around a value. A value would be something like a 'Y'. + * + * @param value to wrap + */ + Token(final Object value) { + this.value = value; + this.count = 1; + } + + /** + * Wraps a token around a repeated number of a value, for example it would + * store 'yyyy' as a value for y and a count of 4. + * + * @param value to wrap + * @param count to wrap + */ + Token(final Object value, final int count) { + this.value = value; + this.count = count; + } + + /** + * Adds another one of the value + */ + void increment() { + count++; + } + + /** + * Gets the current number of values represented + * + * @return int number of values represented + */ + int getCount() { + return count; + } + + /** + * Gets the particular value this token represents. + * + * @return Object value + */ + Object getValue() { + return value; + } + + /** + * Supports equality of this Token to another Token. + * + * @param obj2 Object to consider equality of + * @return boolean true if equal + */ + @Override + public boolean equals(final Object obj2) { + if (obj2 instanceof Token) { + final Token tok2 = (Token) obj2; + if (this.value.getClass() != tok2.value.getClass()) { + return false; + } + if (this.count != tok2.count) { + return false; + } + if (this.value instanceof StringBuilder) { + return this.value.toString().equals(tok2.value.toString()); + } else if (this.value instanceof Number) { + return this.value.equals(tok2.value); + } else { + return this.value == tok2.value; + } + } + return false; + } + + /** + * Returns a hash code for the token equal to the + * hash code for the token's value. Thus 'TT' and 'TTTT' + * will have the same hash code. + * + * @return The hash code for the token + */ + @Override + public int hashCode() { + return this.value.hashCode(); + } + + /** + * Represents this token as a String. + * + * @return String representation of the token + */ + @Override + public String toString() { + return StringUtils.repeat(this.value.toString(), this.count); + } + } + +} diff --git a/Java/commons-lang-DurationFormatUtils_552/metadata.json b/Java/commons-lang-DurationFormatUtils_552/metadata.json new file mode 100644 index 000000000..7f086cf25 --- /dev/null +++ b/Java/commons-lang-DurationFormatUtils_552/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-DurationFormatUtils_552", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/time/DurationFormatUtils.java", + "line": 560, + "npe_method": "lexx", + "deref_field": "value", + "npe_class": "DurationFormatUtils", + "repo": "commons-lang", + "bug_id": "DurationFormatUtils_552" + } +} diff --git a/Java/commons-lang-DurationFormatUtils_552/npe.json b/Java/commons-lang-DurationFormatUtils_552/npe.json new file mode 100644 index 000000000..5e2e3d76e --- /dev/null +++ b/Java/commons-lang-DurationFormatUtils_552/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/time/DurationFormatUtils.java", + "line": 560, + "npe_method": "lexx", + "deref_field": "value", + "npe_class": "DurationFormatUtils" +} \ No newline at end of file