You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2018/03/21 16:44:37 UTC
[07/13] groovy git commit: move datetime extensions to their own
module
http://git-wip-us.apache.org/repos/asf/groovy/blob/48a54936/subprojects/groovy-dateutil/src/main/java/org/apache/groovy/dateutil/extensions/DateUtilExtensions.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-dateutil/src/main/java/org/apache/groovy/dateutil/extensions/DateUtilExtensions.java b/subprojects/groovy-dateutil/src/main/java/org/apache/groovy/dateutil/extensions/DateUtilExtensions.java
new file mode 100644
index 0000000..19cf6dd
--- /dev/null
+++ b/subprojects/groovy-dateutil/src/main/java/org/apache/groovy/dateutil/extensions/DateUtilExtensions.java
@@ -0,0 +1,778 @@
+/*
+ * 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.groovy.dateutil.extensions;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport;
+
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * This class defines new groovy methods which appear on normal JDK
+ * Date and Calendar classes inside the Groovy environment.
+ */
+public class DateUtilExtensions extends DefaultGroovyMethodsSupport {
+
+ /**
+ * Support the subscript operator for a Date.
+ *
+ * @param self a Date
+ * @param field a Calendar field, e.g. MONTH
+ * @return the value for the given field, e.g. FEBRUARY
+ * @see java.util.Calendar
+ * @since 1.5.5
+ */
+ public static int getAt(Date self, int field) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ return cal.get(field);
+ }
+
+ /**
+ * Convert a Date to a Calendar.
+ *
+ * @param self a Date
+ * @return a Calendar corresponding to the given Date
+ * @since 1.7.6
+ */
+ public static Calendar toCalendar(Date self) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ return cal;
+ }
+
+ /**
+ * Support the subscript operator for a Calendar.
+ *
+ * @param self a Calendar
+ * @param field a Calendar field, e.g. MONTH
+ * @return the value for the given field, e.g. FEBRUARY
+ * @see java.util.Calendar
+ * @since 1.7.3
+ */
+ public static int getAt(Calendar self, int field) {
+ return self.get(field);
+ }
+
+ /**
+ * Support the subscript operator for mutating a Calendar.
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.*
+ * def cal = Calendar.instance
+ * cal[DAY_OF_WEEK] = MONDAY
+ * cal[MONTH] = MARCH
+ * println cal.time // A Monday in March
+ * </pre>
+ *
+ * @param self A Calendar
+ * @param field A Calendar field, e.g. MONTH
+ * @param value The value for the given field, e.g. FEBRUARY
+ * @see java.util.Calendar#set(int, int)
+ * @since 1.7.3
+ */
+ public static void putAt(Calendar self, int field, int value) {
+ self.set(field, value);
+ }
+
+ /**
+ * Support the subscript operator for mutating a Date.
+ *
+ * @param self A Date
+ * @param field A Calendar field, e.g. MONTH
+ * @param value The value for the given field, e.g. FEBRUARY
+ * @see #putAt(java.util.Calendar, int, int)
+ * @see java.util.Calendar#set(int, int)
+ * @since 1.7.3
+ */
+ public static void putAt(Date self, int field, int value) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ putAt(cal, field, value);
+ self.setTime(cal.getTimeInMillis());
+ }
+
+ /**
+ * Support mutating a Calendar with a Map.
+ * <p>
+ * The map values are the normal values provided as the
+ * second parameter to <code>java.util.Calendar#set(int, int)</code>.
+ * The keys can either be the normal fields values provided as
+ * the first parameter to that method or one of the following Strings:
+ * <table border="1" cellpadding="4">
+ * <caption>Calendar index values</caption>
+ * <tr><td>year</td><td>Calendar.YEAR</td></tr>
+ * <tr><td>month</td><td>Calendar.MONTH</td></tr>
+ * <tr><td>date</td><td>Calendar.DATE</td></tr>
+ * <tr><td>dayOfMonth</td><td>Calendar.DATE</td></tr>
+ * <tr><td>hourOfDay</td><td>Calendar.HOUR_OF_DAY</td></tr>
+ * <tr><td>minute</td><td>Calendar.MINUTE</td></tr>
+ * <tr><td>second</td><td>Calendar.SECOND</td></tr>
+ * </table>
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.*
+ * def cal = Calendar.instance
+ * def m = [:]
+ * m[YEAR] = 2010
+ * m[MONTH] = DECEMBER
+ * m[DATE] = 25
+ * cal.set(m)
+ * println cal.time // Christmas 2010
+ *
+ * cal.set(year:2011, month:DECEMBER, date:25)
+ * println cal.time // Christmas 2010
+ * </pre>
+ *
+ * @param self A Calendar
+ * @param updates A Map of Calendar keys and values
+ * @see java.util.Calendar#set(int, int)
+ * @see java.util.Calendar#set(int, int, int, int, int, int)
+ * @since 1.7.3
+ */
+ public static void set(Calendar self, Map<Object, Integer> updates) {
+ for (Map.Entry<Object, Integer> entry : updates.entrySet()) {
+ Object key = entry.getKey();
+ if (key instanceof String) key = CAL_MAP.get(key);
+ if (key instanceof Integer) self.set((Integer) key, entry.getValue());
+ }
+ }
+
+ /**
+ * Legacy alias for copyWith. Will be deprecated and removed in future versions of Groovy.
+ *
+ * @see #copyWith(java.util.Calendar, java.util.Map)
+ * @since 1.7.3
+ */
+ public static Calendar updated(Calendar self, Map<Object, Integer> updates) {
+ Calendar result = (Calendar) self.clone();
+ set(result, updates);
+ return result;
+ }
+
+ /**
+ * Support creating a new Date having similar properties to
+ * an existing Date (which remains unaltered) but with
+ * some fields updated according to a Map of changes.
+ * <p>
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.YEAR
+ * def now = Calendar.instance
+ * def nextYear = now[YEAR] + 1
+ * def oneYearFromNow = now.copyWith(year: nextYear)
+ * println now.time
+ * println oneYearFromNow.time
+ * </pre>
+ *
+ * @param self A Calendar
+ * @param updates A Map of Calendar keys and values
+ * @return The newly created Calendar
+ * @see java.util.Calendar#set(int, int)
+ * @see java.util.Calendar#set(int, int, int, int, int, int)
+ * @see #set(java.util.Calendar, java.util.Map)
+ * @since 2.2.0
+ */
+ public static Calendar copyWith(Calendar self, Map<Object, Integer> updates) {
+ Calendar result = (Calendar) self.clone();
+ set(result, updates);
+ return result;
+ }
+
+ /**
+ * Support mutating a Date with a Map.
+ * <p>
+ * The map values are the normal values provided as the
+ * second parameter to <code>java.util.Calendar#set(int, int)</code>.
+ * The keys can either be the normal fields values provided as
+ * the first parameter to that method or one of the following Strings:
+ * <table border="1" cellpadding="4">
+ * <caption>Calendar index values</caption>
+ * <tr><td>year</td><td>Calendar.YEAR</td></tr>
+ * <tr><td>month</td><td>Calendar.MONTH</td></tr>
+ * <tr><td>date</td><td>Calendar.DATE</td></tr>
+ * <tr><td>dayOfMonth</td><td>Calendar.DATE</td></tr>
+ * <tr><td>hourOfDay</td><td>Calendar.HOUR_OF_DAY</td></tr>
+ * <tr><td>minute</td><td>Calendar.MINUTE</td></tr>
+ * <tr><td>second</td><td>Calendar.SECOND</td></tr>
+ * </table>
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.YEAR
+ * def date = new Date()
+ * def nextYear = date[YEAR] + 1
+ * date.set(year: nextYear)
+ * println date
+ * </pre>
+ *
+ * @param self A Date
+ * @param updates A Map of Calendar keys and values
+ * @see java.util.Calendar#set(int, int)
+ * @see #set(java.util.Calendar, java.util.Map)
+ * @since 1.7.3
+ */
+ public static void set(Date self, Map<Object, Integer> updates) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ set(cal, updates);
+ self.setTime(cal.getTimeInMillis());
+ }
+
+ /**
+ * Legacy alias for copyWith. Will be deprecated and removed in future versions of Groovy.
+ *
+ * @see #copyWith(java.util.Date, java.util.Map)
+ * @since 1.7.3
+ */
+ public static Date updated(Date self, Map<Object, Integer> updates) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ set(cal, updates);
+ return cal.getTime();
+ }
+
+ /**
+ * Support creating a new Date having similar properties to
+ * an existing Date (which remains unaltered) but with
+ * some fields updated according to a Map of changes.
+ * <p>
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.YEAR
+ * def today = new Date()
+ * def nextYear = today[YEAR] + 1
+ * def oneYearFromNow = today.copyWith(year: nextYear)
+ * println today
+ * println oneYearFromNow
+ * </pre>
+ *
+ * @param self A Date
+ * @param updates A Map of Calendar keys and values
+ * @return The newly created Date
+ * @see java.util.Calendar#set(int, int)
+ * @see #set(java.util.Date, java.util.Map)
+ * @see #copyWith(java.util.Calendar, java.util.Map)
+ * @since 2.2.0
+ */
+ public static Date copyWith(Date self, Map<Object, Integer> updates) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ set(cal, updates);
+ return cal.getTime();
+ }
+
+ private static final Map<String, Integer> CAL_MAP = new HashMap<String, Integer>();
+
+ static {
+ CAL_MAP.put("year", Calendar.YEAR);
+ CAL_MAP.put("month", Calendar.MONTH);
+ CAL_MAP.put("date", Calendar.DATE);
+ CAL_MAP.put("dayOfMonth", Calendar.DATE);
+ CAL_MAP.put("hourOfDay", Calendar.HOUR_OF_DAY);
+ CAL_MAP.put("minute", Calendar.MINUTE);
+ CAL_MAP.put("second", Calendar.SECOND);
+ }
+
+ /**
+ * Increment a Date by one day.
+ *
+ * @param self a Date
+ * @return the next days date
+ * @since 1.0
+ */
+ public static Date next(Date self) {
+ return plus(self, 1);
+ }
+
+ /**
+ * Increment a Calendar by one day.
+ *
+ * @param self a Calendar
+ * @return a new Calendar set to the next day
+ * @since 1.8.7
+ */
+ public static Calendar next(Calendar self) {
+ Calendar result = (Calendar) self.clone();
+ result.add(Calendar.DATE, 1);
+ return result;
+ }
+
+ /**
+ * Decrement a Calendar by one day.
+ *
+ * @param self a Calendar
+ * @return a new Calendar set to the previous day
+ * @since 1.8.7
+ */
+ public static Calendar previous(Calendar self) {
+ Calendar result = (Calendar) self.clone();
+ result.add(Calendar.DATE, -1);
+ return result;
+ }
+
+ /**
+ * Increment a java.sql.Date by one day.
+ *
+ * @param self a java.sql.Date
+ * @return the next days date
+ * @since 1.0
+ */
+ public static java.sql.Date next(java.sql.Date self) {
+ return new java.sql.Date(next((Date) self).getTime());
+ }
+
+ /**
+ * Decrement a Date by one day.
+ *
+ * @param self a Date
+ * @return the previous days date
+ * @since 1.0
+ */
+ public static Date previous(Date self) {
+ return minus(self, 1);
+ }
+
+ /**
+ * Decrement a java.sql.Date by one day.
+ *
+ * @param self a java.sql.Date
+ * @return the previous days date
+ * @since 1.0
+ */
+ public static java.sql.Date previous(java.sql.Date self) {
+ return new java.sql.Date(previous((Date) self).getTime());
+ }
+
+ /**
+ * Add a number of days to this date and returns the new date.
+ *
+ * @param self a Date
+ * @param days the number of days to increase
+ * @return the new date
+ * @since 1.0
+ */
+ public static Date plus(Date self, int days) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(self);
+ calendar.add(Calendar.DATE, days);
+ return calendar.getTime();
+ }
+
+ /**
+ * Add a number of days to this date and returns the new date.
+ *
+ * @param self a java.sql.Date
+ * @param days the number of days to increase
+ * @return the new date
+ * @since 1.0
+ */
+ public static java.sql.Date plus(java.sql.Date self, int days) {
+ return new java.sql.Date(plus((Date) self, days).getTime());
+ }
+
+ /**
+ * Add number of days to this Timestamp and returns the new Timestamp object.
+ *
+ * @param self a Timestamp
+ * @param days the number of days to increase
+ * @return the new Timestamp
+ */
+ public static Timestamp plus(Timestamp self, int days) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(self);
+ calendar.add(Calendar.DATE, days);
+ Timestamp ts = new Timestamp(calendar.getTime().getTime());
+ ts.setNanos(self.getNanos());
+ return ts;
+ }
+
+ /**
+ * Subtract a number of days from this date and returns the new date.
+ *
+ * @param self a Date
+ * @param days the number of days to subtract
+ * @return the new date
+ * @since 1.0
+ */
+ public static Date minus(Date self, int days) {
+ return plus(self, -days);
+ }
+
+ /**
+ * Subtract a number of days from this date and returns the new date.
+ *
+ * @param self a java.sql.Date
+ * @param days the number of days to subtract
+ * @return the new date
+ * @since 1.0
+ */
+ public static java.sql.Date minus(java.sql.Date self, int days) {
+ return new java.sql.Date(minus((Date) self, days).getTime());
+ }
+
+ /**
+ * Subtract a number of days from this Timestamp and returns the new Timestamp object.
+ *
+ * @param self a Timestamp
+ * @param days the number of days to subtract
+ * @return the new Timestamp
+ */
+ public static Timestamp minus(Timestamp self, int days) {
+ return plus(self, -days);
+ }
+
+ /**
+ * Subtract another date from this one and return the number of days of the difference.
+ * <p>
+ * Date self = Date then + (Date self - Date then)
+ * <p>
+ * IOW, if self is before then the result is a negative value.
+ *
+ * @param self a Calendar
+ * @param then another Calendar
+ * @return number of days
+ * @since 1.6.0
+ */
+ public static int minus(Calendar self, Calendar then) {
+ Calendar a = self;
+ Calendar b = then;
+
+ boolean swap = a.before(b);
+
+ if (swap) {
+ Calendar t = a;
+ a = b;
+ b = t;
+ }
+
+ int days = 0;
+
+ b = (Calendar) b.clone();
+
+ while (a.get(Calendar.YEAR) > b.get(Calendar.YEAR)) {
+ days += 1 + (b.getActualMaximum(Calendar.DAY_OF_YEAR) - b.get(Calendar.DAY_OF_YEAR));
+ b.set(Calendar.DAY_OF_YEAR, 1);
+ b.add(Calendar.YEAR, 1);
+ }
+
+ days += a.get(Calendar.DAY_OF_YEAR) - b.get(Calendar.DAY_OF_YEAR);
+
+ if (swap) days = -days;
+
+ return days;
+ }
+
+ /**
+ * Subtract another Date from this one and return the number of days of the difference.
+ * <p>
+ * Date self = Date then + (Date self - Date then)
+ * <p>
+ * IOW, if self is before then the result is a negative value.
+ *
+ * @param self a Date
+ * @param then another Date
+ * @return number of days
+ * @since 1.6.0
+ */
+ public static int minus(Date self, Date then) {
+ Calendar a = (Calendar) Calendar.getInstance().clone();
+ a.setTime(self);
+ Calendar b = (Calendar) Calendar.getInstance().clone();
+ b.setTime(then);
+ return minus(a, b);
+ }
+
+ /**
+ * <p>Create a String representation of this date according to the given
+ * format pattern.
+ * <p>
+ * <p>For example, if the system timezone is GMT,
+ * <code>new Date(0).format('MM/dd/yy')</code> would return the string
+ * <code>"01/01/70"</code>. See documentation for {@link java.text.SimpleDateFormat}
+ * for format pattern use.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @param format the format pattern to use according to {@link java.text.SimpleDateFormat}
+ * @return a string representation of this date.
+ * @see java.text.SimpleDateFormat
+ * @since 1.5.7
+ */
+ public static String format(Date self, String format) {
+ return new SimpleDateFormat(format).format(self);
+ }
+
+ /**
+ * <p>Create a String representation of this date according to the given
+ * format pattern and timezone.
+ * <p>
+ * <p>For example:
+ * <code>
+ * def d = new Date(0)
+ * def tz = TimeZone.getTimeZone('GMT')
+ * println d.format('dd/MMM/yyyy', tz)
+ * </code> would return the string
+ * <code>"01/Jan/1970"</code>. See documentation for {@link java.text.SimpleDateFormat}
+ * for format pattern use.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @param format the format pattern to use according to {@link java.text.SimpleDateFormat}
+ * @param tz the TimeZone to use
+ * @return a string representation of this date.
+ * @see java.text.SimpleDateFormat
+ * @since 1.8.3
+ */
+ public static String format(Date self, String format, TimeZone tz) {
+ SimpleDateFormat sdf = new SimpleDateFormat(format);
+ sdf.setTimeZone(tz);
+ return sdf.format(self);
+ }
+
+ /**
+ * <p>Return a string representation of the 'day' portion of this date
+ * according to the locale-specific {@link java.text.DateFormat#SHORT} default format.
+ * For an "en_UK" system locale, this would be <code>dd/MM/yy</code>.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @return a string representation of this date
+ * @see java.text.DateFormat#getDateInstance(int)
+ * @see java.text.DateFormat#SHORT
+ * @since 1.5.7
+ */
+ public static String getDateString(Date self) {
+ return DateFormat.getDateInstance(DateFormat.SHORT).format(self);
+ }
+
+ /**
+ * <p>Return a string representation of the time portion of this date
+ * according to the locale-specific {@link java.text.DateFormat#MEDIUM} default format.
+ * For an "en_UK" system locale, this would be <code>HH:MM:ss</code>.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @return a string representing the time portion of this date
+ * @see java.text.DateFormat#getTimeInstance(int)
+ * @see java.text.DateFormat#MEDIUM
+ * @since 1.5.7
+ */
+ public static String getTimeString(Date self) {
+ return DateFormat.getTimeInstance(DateFormat.MEDIUM).format(self);
+ }
+
+ /**
+ * <p>Return a string representation of the date and time time portion of
+ * this Date instance, according to the locale-specific format used by
+ * {@link java.text.DateFormat}. This method uses the {@link java.text.DateFormat#SHORT}
+ * preset for the day portion and {@link java.text.DateFormat#MEDIUM} for the time
+ * portion of the output string.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @return a string representation of this date and time
+ * @see java.text.DateFormat#getDateTimeInstance(int, int)
+ * @since 1.5.7
+ */
+ public static String getDateTimeString(Date self) {
+ return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(self);
+ }
+
+ /**
+ * Common code for {@link #clearTime(java.util.Calendar)} and {@link #clearTime(java.util.Date)}
+ * and {@link #clearTime(java.sql.Date)}
+ *
+ * @param self a Calendar to adjust
+ */
+ private static void clearTimeCommon(final Calendar self) {
+ self.set(Calendar.HOUR_OF_DAY, 0);
+ self.clear(Calendar.MINUTE);
+ self.clear(Calendar.SECOND);
+ self.clear(Calendar.MILLISECOND);
+ }
+
+ /**
+ * Clears the time portion of this Date instance; useful utility where
+ * it makes sense to compare month/day/year only portions of a Date.
+ *
+ * @param self a Date
+ * @return the Date but with the time portion cleared
+ * @since 1.6.7
+ */
+ public static Date clearTime(final Date self) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(self);
+ clearTimeCommon(calendar);
+ self.setTime(calendar.getTime().getTime());
+ return self;
+ }
+
+ /**
+ * Clears the time portion of this java.sql.Date instance; useful utility
+ * where it makes sense to compare month/day/year only portions of a Date.
+ *
+ * @param self a java.sql.Date
+ * @return the java.sql.Date but with the time portion cleared
+ * @since 1.6.7
+ */
+ public static java.sql.Date clearTime(final java.sql.Date self) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(self);
+ clearTimeCommon(calendar);
+ self.setTime(calendar.getTime().getTime());
+ return self;
+ }
+
+ /**
+ * Clears the time portion of this Calendar instance; useful utility
+ * where it makes sense to compare month/day/year only portions of a Calendar.
+ *
+ * @param self a Calendar
+ * @return the Calendar but with the time portion cleared
+ * @since 1.6.7
+ */
+ public static Calendar clearTime(final Calendar self) {
+ clearTimeCommon(self);
+ return self;
+ }
+
+ /**
+ * <p>Shortcut for {@link java.text.SimpleDateFormat} to output a String representation
+ * of this calendar instance. This method respects the Calendar's assigned
+ * {@link java.util.TimeZone}, whereas calling <code>cal.time.format('HH:mm:ss')</code>
+ * would use the system timezone.
+ * <p>Note that Calendar equivalents of <code>date.getDateString()</code>
+ * and variants do not exist because those methods are Locale-dependent.
+ * Although a Calendar may be assigned a {@link java.util.Locale}, that information is
+ * lost and therefore cannot be used to control the default date/time formats
+ * provided by these methods. Instead, the system Locale would always be
+ * used. The alternative is to simply call
+ * {@link java.text.DateFormat#getDateInstance(int, java.util.Locale)} and pass the same Locale
+ * that was used for the Calendar.
+ *
+ * @param self this calendar
+ * @param pattern format pattern
+ * @return String representation of this calendar with the given format.
+ * @see java.text.DateFormat#setTimeZone(java.util.TimeZone)
+ * @see java.text.SimpleDateFormat#format(java.util.Date)
+ * @see #format(java.util.Date, String)
+ * @since 1.6.0
+ */
+ public static String format(Calendar self, String pattern) {
+ SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+ sdf.setTimeZone(self.getTimeZone());
+ return sdf.format(self.getTime());
+ }
+
+ /**
+ * Iterates from this date up to the given date, inclusive,
+ * incrementing by one day each time.
+ *
+ * @param self a Date
+ * @param to another Date to go up to
+ * @param closure the closure to call
+ * @since 2.2
+ */
+ public static void upto(Date self, Date to, Closure closure) {
+ if (self.compareTo(to) <= 0) {
+ for (Date i = (Date) self.clone(); i.compareTo(to) <= 0; i = next(i)) {
+ closure.call(i);
+ }
+ } else
+ throw new GroovyRuntimeException("The argument (" + to +
+ ") to upto() cannot be earlier than the value (" + self + ") it's called on.");
+ }
+
+ /**
+ * Iterates from the date represented by this calendar up to the date represented
+ * by the given calendar, inclusive, incrementing by one day each time.
+ *
+ * @param self a Calendar
+ * @param to another Calendar to go up to
+ * @param closure the closure to call
+ * @since 2.2
+ */
+ public static void upto(Calendar self, Calendar to, Closure closure) {
+ if (self.compareTo(to) <= 0) {
+ for (Calendar i = (Calendar) self.clone(); i.compareTo(to) <= 0; i = next(i)) {
+ closure.call(i);
+ }
+ } else
+ throw new GroovyRuntimeException("The argument (" + to +
+ ") to upto() cannot be earlier than the value (" + self + ") it's called on.");
+ }
+
+ /**
+ * Iterates from this date down to the given date, inclusive,
+ * decrementing by one day each time.
+ *
+ * @param self a Date
+ * @param to another Date to go down to
+ * @param closure the closure to call
+ * @since 2.2
+ */
+ public static void downto(Date self, Date to, Closure closure) {
+ if (self.compareTo(to) >= 0) {
+ for (Date i = (Date) self.clone(); i.compareTo(to) >= 0; i = previous(i)) {
+ closure.call(i);
+ }
+ } else
+ throw new GroovyRuntimeException("The argument (" + to +
+ ") to downto() cannot be later than the value (" + self + ") it's called on.");
+ }
+
+ /**
+ * Iterates from the date represented by this calendar up to the date represented
+ * by the given calendar, inclusive, incrementing by one day each time.
+ *
+ * @param self a Calendar
+ * @param to another Calendar to go down to
+ * @param closure the closure to call
+ * @since 2.2
+ */
+ public static void downto(Calendar self, Calendar to, Closure closure) {
+ if (self.compareTo(to) >= 0) {
+ for (Calendar i = (Calendar) self.clone(); i.compareTo(to) >= 0; i = previous(i)) {
+ closure.call(i);
+ }
+ } else
+ throw new GroovyRuntimeException("The argument (" + to +
+ ") to downto() cannot be later than the value (" + self + ") it's called on.");
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/48a54936/subprojects/groovy-dateutil/src/spec/doc/working-with-dateutil-types.adoc
----------------------------------------------------------------------
diff --git a/subprojects/groovy-dateutil/src/spec/doc/working-with-dateutil-types.adoc b/subprojects/groovy-dateutil/src/spec/doc/working-with-dateutil-types.adoc
new file mode 100644
index 0000000..9f26972
--- /dev/null
+++ b/subprojects/groovy-dateutil/src/spec/doc/working-with-dateutil-types.adoc
@@ -0,0 +1,54 @@
+//////////////////////////////////////////
+
+ 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.
+
+//////////////////////////////////////////
+
+= Working with legacy Date/Calendar types
+
+The `groovy-dateutil` module supports numerous extensions for working with
+Java's classic `Date` and `Calendar` classes.
+
+You can access the properties of a `Date` or `Calendar` using the normal array index notation
+with the constant field numbers from the `Calendar` class as shown in the following example:
+
+[source,groovy]
+-------------------------------------
+include::{projectdir}/src/spec/test/gdk/WorkingWithDateUtilTypesTest.groovy[tags=calendar_getAt,indent=0]
+-------------------------------------
+<1> Import the constants
+<2> Setting the calendar's year, month and day of month
+<3> Accessing the calendar's day of week
+
+Groovy supports arithmetic on and iteration between `Date` and `Calendar` instances as shown in the following example:
+[source,groovy]
+-------------------------------------
+include::{projectdir}/src/spec/test/gdk/WorkingWithDateUtilTypesTest.groovy[tags=date_arithmetic,indent=0]
+-------------------------------------
+
+You can parse strings into dates and output dates into formatted strings:
+[source,groovy]
+-------------------------------------
+include::{projectdir}/src/spec/test/gdk/WorkingWithDateUtilTypesTest.groovy[tags=date_parsing,indent=0]
+-------------------------------------
+
+You can also create a new Date or Calendar based on an existing one:
+[source,groovy]
+-------------------------------------
+include::{projectdir}/src/spec/test/gdk/WorkingWithDateUtilTypesTest.groovy[tags=date_copyWith,indent=0]
+-------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/48a54936/subprojects/groovy-dateutil/src/spec/test/gdk/WorkingWithDateUtilTypesTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-dateutil/src/spec/test/gdk/WorkingWithDateUtilTypesTest.groovy b/subprojects/groovy-dateutil/src/spec/test/gdk/WorkingWithDateUtilTypesTest.groovy
new file mode 100644
index 0000000..7599867
--- /dev/null
+++ b/subprojects/groovy-dateutil/src/spec/test/gdk/WorkingWithDateUtilTypesTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * 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 gdk
+
+class WorkingWithDateUtilTypesTest extends GroovyTestCase {
+
+ void testGetAt() {
+ assertScript '''
+ // tag::calendar_getAt[]
+ import static java.util.Calendar.* // <1>
+
+ def cal = Calendar.instance
+ cal[YEAR] = 2000 // <2>
+ cal[MONTH] = JANUARY // <2>
+ cal[DAY_OF_MONTH] = 1 // <2>
+ assert cal[DAY_OF_WEEK] == SATURDAY // <3>
+ // end::calendar_getAt[]
+ '''
+ }
+
+ void testDateArithmetic() {
+ // tag::date_arithmetic[]
+ def yesterday = new Date() - 1
+ def tomorrow = new Date() + 1
+
+ def diffInDays = tomorrow - yesterday
+ assert diffInDays == 2
+
+ int count = 0
+ yesterday.upto(tomorrow) { count++ }
+ assert count == 3
+ // end::date_arithmetic[]
+ }
+
+ void testDateParsing() {
+ assertScript '''
+ import static java.util.Calendar.*
+
+ // tag::date_parsing[]
+ def orig = '2000-01-01'
+ def newYear = Date.parse('yyyy-MM-dd', orig)
+ assert newYear[DAY_OF_WEEK] == SATURDAY
+ assert newYear.format('yyyy-MM-dd') == orig
+ assert newYear.format('dd/MM/yyyy') == '01/01/2000'
+ // end::date_parsing[]
+ '''
+ }
+
+ void testCopyWith() {
+ assertScript '''
+ import static java.util.Calendar.*
+
+ // tag::date_copyWith[]
+ def newYear = Date.parse('yyyy-MM-dd', '2000-01-01')
+ def newYearsEve = newYear.copyWith(
+ year: 1999,
+ month: DECEMBER,
+ dayOfMonth: 31
+ )
+ assert newYearsEve[DAY_OF_WEEK] == FRIDAY
+ // end::date_copyWith[]
+ '''
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/48a54936/subprojects/groovy-dateutil/src/test/java/groovy/DateTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-dateutil/src/test/java/groovy/DateTest.groovy b/subprojects/groovy-dateutil/src/test/java/groovy/DateTest.groovy
new file mode 100644
index 0000000..b014f53
--- /dev/null
+++ b/subprojects/groovy-dateutil/src/test/java/groovy/DateTest.groovy
@@ -0,0 +1,327 @@
+/*
+ * 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 groovy
+
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+
+import static java.util.Calendar.*
+
+class DateTest extends GroovyTestCase {
+ void testCalendarNextPrevious() {
+ TimeZone tz = TimeZone.getTimeZone('GMT+00')
+ Calendar c = getInstance(tz)
+ c[HOUR_OF_DAY] = 6
+ c[YEAR] = 2002
+ c[MONTH] = FEBRUARY
+ c[DATE] = 2
+ c.clearTime()
+ def formatter = new SimpleDateFormat('dd-MMM-yyyy', Locale.US)
+ formatter.calendar.timeZone = tz
+
+ assert formatter.format(c.previous().time) == '01-Feb-2002'
+ assert formatter.format(c.time) == '02-Feb-2002'
+ assert formatter.format(c.next().time) == '03-Feb-2002'
+ def dates = (c.previous()..c.next()).collect{ formatter.format(it.time) }
+ assert dates == ['01-Feb-2002', '02-Feb-2002', '03-Feb-2002']
+ }
+
+ void testDateNextPrevious() {
+ def tz = TimeZone.default
+ def x = new Date()
+ def y = x + 2
+ assert x < y
+ def crossedDaylightSavingBoundary = tz.inDaylightTime(x) ^ tz.inDaylightTime(y)
+ ++x
+ --y
+ if (!crossedDaylightSavingBoundary) assert x == y
+ x += 2
+ assert x > y
+ }
+
+ void testDateRange() {
+ def today = new Date()
+ def later = today + 3
+ def expected = [today, today + 1, today + 2, today + 3]
+ def list = []
+ for (d in today..later) {
+ list << d
+ }
+ assert list == expected
+ }
+
+ void testCalendarIndex() {
+ Calendar c = new GregorianCalendar(2002, FEBRUARY, 2)
+ assert c[MONTH] == FEBRUARY
+ assert c[DAY_OF_WEEK] == SATURDAY
+ }
+
+ void testDateIndex() {
+ Date d = new GregorianCalendar(2002, FEBRUARY, 2).time
+ assert d[MONTH] == FEBRUARY
+ assert d[DAY_OF_WEEK] == SATURDAY
+ }
+
+ void testGDKDateMethods() {
+ Locale defaultLocale = Locale.default
+ TimeZone defaultTZ = TimeZone.default
+ try {
+ Locale locale = Locale.GERMANY
+ Locale.setDefault locale // set this otherwise the test will fail if your locale isn't the same
+ TimeZone.setDefault TimeZone.getTimeZone('Europe/Berlin')
+
+ Date d = new Date(0)
+
+ assertEquals '1970-01-01', d.format('yyyy-MM-dd')
+ assertEquals '01/01/1970', d.format('dd/MM/yyyy', TimeZone.getTimeZone('GMT'))
+ assertEquals DateFormat.getDateInstance(DateFormat.SHORT, locale).format(d), d.dateString
+ assertEquals '01.01.70', d.dateString
+ assertEquals DateFormat.getTimeInstance(DateFormat.MEDIUM, locale).format(d), d.timeString
+ assertEquals '01:00:00', d.timeString
+ assertEquals DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale).format(d), d.dateTimeString
+ } finally {
+ Locale.default = defaultLocale
+ TimeZone.setDefault defaultTZ
+ }
+ }
+
+ void testStaticParse() {
+ TimeZone defaultTZ = TimeZone.default
+ try {
+ TimeZone.setDefault TimeZone.getTimeZone('Etc/GMT')
+
+ Date d = Date.parse('yy/MM/dd hh:mm:ss', '70/01/01 00:00:00')
+
+ assertEquals 0, d.time
+ } finally {
+ TimeZone.setDefault defaultTZ
+ }
+ }
+
+ void testParseWithTimeZone() {
+ TimeZone defaultTZ = TimeZone.default
+ try {
+ TimeZone.default = TimeZone.getTimeZone("GMT+05")
+ def tz = TimeZone.getTimeZone("GMT+03")
+
+ def newYear = Date.parse('yyyy-MM-dd', "2015-01-01", tz)
+
+ assert newYear.toString() == 'Thu Jan 01 02:00:00 GMT+05:00 2015'
+ } finally {
+ TimeZone.default = defaultTZ
+ }
+ }
+
+ void testRoundTrip() {
+ Date d = new Date()
+ String pattern = 'dd MMM yyyy, hh:mm:ss,SSS a z'
+ String out = d.format(pattern)
+
+ Date d2 = Date.parse(pattern, out)
+
+ assertEquals d.time, d2.time
+ }
+
+ void testCalendarTimeZone() {
+ Locale defaultLocale = Locale.default
+ TimeZone defaultTZ = TimeZone.default
+ try {
+ Locale locale = Locale.UK
+ Locale.setDefault locale // set this otherwise the test will fail if your locale isn't the same
+ TimeZone.setDefault TimeZone.getTimeZone('Etc/GMT')
+
+ def offset = 8
+ def notLocalTZ = TimeZone.getTimeZone("GMT-$offset")
+ Calendar cal = Calendar.getInstance(notLocalTZ)
+ def offsetHr = cal.format('HH') as int
+ def hr = cal.time.format('HH') as int
+ if (hr < offset) hr += 24 // if GMT hr has rolled over to next day
+
+ // offset should be 8 hours behind GMT:
+ assertEquals(offset, hr - offsetHr)
+ } finally {
+ Locale.default = defaultLocale
+ TimeZone.setDefault defaultTZ
+ }
+ }
+
+ static SimpleDateFormat f = new SimpleDateFormat('MM/dd/yyyy')
+
+ static java.sql.Date sqlDate(String s) {
+ return new java.sql.Date(f.parse(s).time)
+ }
+
+ void testMinusDates() {
+ assertEquals(10, f.parse("1/11/2007") - f.parse("1/1/2007"))
+ assertEquals(-10, f.parse("1/1/2007") - f.parse("1/11/2007"))
+ assertEquals(375, f.parse("1/11/2008") - f.parse("1/1/2007"))
+ assertEquals(356, f.parse("1/1/2008") - f.parse("1/10/2007"))
+ assertEquals(1, f.parse("7/12/2007") - f.parse("7/11/2007"))
+ assertEquals(0, f.parse("1/1/2007") - f.parse("1/1/2007"))
+ assertEquals(-1, f.parse("12/31/2007") - f.parse("1/1/2008"))
+ assertEquals(365, f.parse("1/1/2008") - f.parse("1/1/2007"))
+ assertEquals(36525, f.parse("1/1/2008") - f.parse("1/1/1908"))
+
+ assertEquals(1, sqlDate("7/12/2007") - f.parse("7/11/2007"))
+ assertEquals(0, sqlDate("1/1/2007") - sqlDate("1/1/2007"))
+ assertEquals(-1, f.parse("12/31/2007") - sqlDate("1/1/2008"))
+ assertEquals(365, sqlDate("1/1/2008") - sqlDate("1/1/2007"))
+ assertEquals(36525, f.parse("1/1/2008") - sqlDate("1/1/1908"))
+
+ Date d = f.parse("7/4/1776");
+ assertEquals(44, (d + 44) - d);
+
+ java.sql.Date sqld = sqlDate("7/4/1776");
+ assertEquals(-4444, (sqld - 4444) - sqld);
+ }
+
+ /** GROOVY-3374 */
+ void testClearTime() {
+ def now = new Date()
+ def calendarNow = Calendar.getInstance()
+
+ now.clearTime()
+ calendarNow.clearTime()
+
+ assert now == calendarNow.time
+
+ assert calendarNow.get(Calendar.HOUR) == 0
+ assert calendarNow.get(Calendar.MINUTE) == 0
+ assert calendarNow.get(Calendar.SECOND) == 0
+ assert calendarNow.get(Calendar.MILLISECOND) == 0
+ }
+
+ /** GROOVY-4789 */
+ void testStaticParseToStringDate() {
+ TimeZone tz = TimeZone.getDefault()
+ try {
+ TimeZone.setDefault(TimeZone.getTimeZone("GMT"))
+
+ Date date = new Date(0)
+ String toStringRepresentation = date.toString()
+
+ assert toStringRepresentation == "Thu Jan 01 00:00:00 GMT 1970"
+ assert date == Date.parseToStringDate(toStringRepresentation)
+ }
+ finally {
+ TimeZone.setDefault(tz)
+ }
+ }
+
+ void test_Upto_Date_ShouldExecuteClosureForEachDayUpToDate() {
+ Date startDate = new Date()
+ List expectedResults = [startDate, startDate + 1, startDate + 2]
+
+ List actualResults = []
+
+ startDate.upto(startDate + 2){
+ actualResults << it
+ }
+
+ assert actualResults == expectedResults
+ }
+
+ void test_upto_Date_ShouldNotAcceptToDatesLessThanStartDate() {
+ Date startDate = new Date()
+ Date toDate = new Date(startDate.getTime() - 1L)
+
+ shouldFail(GroovyRuntimeException) {
+ startDate.upto(toDate){}
+ }
+ }
+
+ void test_downto_Date_ShouldExecuteClosureForEachDayDownToDate() {
+ Date startDate = new Date()
+ List expectedResults = [startDate, startDate - 1, startDate - 2]
+
+ List actualResults = []
+ startDate.downto(startDate - 2){
+ actualResults << it
+ }
+
+ assert actualResults == expectedResults
+ }
+
+ void test_downto_Date_ShouldNotAcceptToDatesGreaterThanStartDate() {
+ Date startDate = new Date()
+ Date toDate = new Date(startDate.getTime() + 1L)
+
+ shouldFail(GroovyRuntimeException) {
+ startDate.downto(toDate){}
+ }
+ }
+
+ void test_upto_Calendar_ShouldExecuteClosureForEachDayUpToDate() {
+ Calendar startDate = Calendar.getInstance()
+ Calendar toDate = startDate.clone()
+ toDate.add(Calendar.DATE, 1)
+ List expectedResults = [startDate, toDate]
+
+ List actualResults = []
+ startDate.upto(toDate){
+ actualResults << it
+ }
+
+ assert actualResults == expectedResults
+ }
+
+ void test_upto_Calendar_ShouldNotAcceptToDatesLessThanStartDate() {
+ Calendar startDate = Calendar.getInstance()
+ Calendar toDate = startDate.clone()
+ toDate.add(Calendar.MILLISECOND, -1)
+
+ shouldFail(GroovyRuntimeException) {
+ startDate.upto(toDate){}
+ }
+ }
+
+ void test_downto_Calendar_ShouldExecuteClosureForEachDayDownToDate() {
+ Calendar startDate = Calendar.getInstance()
+ Calendar toDate = startDate.clone()
+ toDate.add(Calendar.DATE, -1)
+ List expectedResults = [startDate, toDate]
+
+ List actualResults = []
+ startDate.downto(toDate){
+ actualResults << it
+ }
+
+ assert actualResults == expectedResults
+ }
+
+ void test_downto_Calendar_ShouldNotAcceptToDatesGreaterThanStartDate() {
+ Calendar startDate = Calendar.getInstance()
+ Calendar toDate = startDate.clone()
+ toDate.add(Calendar.MILLISECOND, 1)
+
+ shouldFail(GroovyRuntimeException) {
+ startDate.downto(toDate){}
+ }
+ }
+
+ void testCopyWith() {
+ Date febOne1970 = new Date(70, 1, 1)
+ Date aprilSix = febOne1970.copyWith(dayOfMonth: 6, month: Calendar.APRIL)
+ assertEquals '1970-04-06', aprilSix.format('yyyy-MM-dd')
+ Map updates = [:]
+ updates[Calendar.DAY_OF_MONTH] = 4
+ Date aprilFour = aprilSix.copyWith(updates)
+ assertEquals '1970-04-04', aprilFour.format('yyyy-MM-dd')
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/48a54936/subprojects/groovy-dateutil/src/test/java/org/apache/groovy/dateutil/extensions/DateUtilExtensionsTest.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-dateutil/src/test/java/org/apache/groovy/dateutil/extensions/DateUtilExtensionsTest.java b/subprojects/groovy-dateutil/src/test/java/org/apache/groovy/dateutil/extensions/DateUtilExtensionsTest.java
new file mode 100644
index 0000000..8cecc5b
--- /dev/null
+++ b/subprojects/groovy-dateutil/src/test/java/org/apache/groovy/dateutil/extensions/DateUtilExtensionsTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.groovy.dateutil.extensions;
+
+import org.junit.Test;
+
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import static org.junit.Assert.assertEquals;
+
+public class DateUtilExtensionsTest {
+ @Test
+ public void plus() throws ParseException {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+ Date dec31 = sdf.parse("20171231");
+ assertEquals("20180101", sdf.format(DateUtilExtensions.plus(dec31, 1)));
+ assertEquals("20180101", sdf.format(DateUtilExtensions.plus(new Timestamp(dec31.getTime()), 1)));
+ }
+
+ @Test
+ public void minus() throws ParseException {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+ Date jan01 = sdf.parse("20180101");
+ assertEquals("20171231", sdf.format(DateUtilExtensions.minus(jan01, 1)));
+ assertEquals("20171231", sdf.format(DateUtilExtensions.minus(new Timestamp(jan01.getTime()), 1)));
+ }
+
+ @Test
+ public void next() throws ParseException {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(sdf.parse("20171231"));
+ assertEquals("20180101", sdf.format(DateUtilExtensions.next(calendar).getTime()));
+ }
+
+ @Test
+ public void previous() throws ParseException {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(sdf.parse("20180101"));
+ assertEquals("20171231", sdf.format(DateUtilExtensions.previous(calendar).getTime()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/48a54936/subprojects/groovy-json/build.gradle
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/build.gradle b/subprojects/groovy-json/build.gradle
index c96a723..c9bf97a 100644
--- a/subprojects/groovy-json/build.gradle
+++ b/subprojects/groovy-json/build.gradle
@@ -20,4 +20,5 @@ dependencies {
compile rootProject
testCompile project(':groovy-test')
testRuntime project(':groovy-ant')
+ testCompile project(':groovy-dateutil')
}