Apache Groovy is a powerful multi-faceted programming language for the JVM platform
—
Time arithmetic, date manipulation, and duration calculations through category-based extensions to Java's Date and temporal classes. These utilities provide intuitive and powerful date/time operations with natural language syntax.
Category class that adds time arithmetic methods to Date and Number classes.
class TimeCategory {
/**
* Adds a duration to a date.
*/
static Date plus(Date date, BaseDuration duration);
/**
* Subtracts a duration from a date.
*/
static Date minus(Date date, BaseDuration duration);
/**
* Calculates the duration between two dates.
*/
static BaseDuration minus(Date lhs, Date rhs);
/**
* Creates a duration from a number of days.
*/
static DatumDependentDuration getDays(Number self);
/**
* Creates a duration from a number of hours.
*/
static BaseDuration getHours(Number self);
/**
* Creates a duration from a number of minutes.
*/
static BaseDuration getMinutes(Number self);
/**
* Creates a duration from a number of seconds.
*/
static BaseDuration getSeconds(Number self);
/**
* Creates a duration from a number of milliseconds.
*/
static BaseDuration getMilliseconds(Number self);
/**
* Creates a duration from a number of weeks.
*/
static DatumDependentDuration getWeeks(Number self);
/**
* Creates a duration from a number of months.
*/
static DatumDependentDuration getMonths(Number self);
/**
* Creates a duration from a number of years.
*/
static DatumDependentDuration getYears(Number self);
/**
* Multiplies a duration by a number.
*/
static BaseDuration multiply(BaseDuration duration, Number factor);
/**
* Divides a duration by a number.
*/
static BaseDuration div(BaseDuration duration, Number divisor);
/**
* Negates a duration.
*/
static BaseDuration negative(BaseDuration duration);
/**
* Gets the absolute value of a duration.
*/
static BaseDuration abs(BaseDuration duration);
}Abstract base classes for representing time durations.
abstract class BaseDuration implements Comparable<BaseDuration> {
/**
* Gets the from date for this duration.
*/
Date getFrom();
/**
* Gets the to date for this duration.
*/
Date getTo();
/**
* Gets the duration in milliseconds.
*/
long toMilliseconds();
/**
* Converts to a string representation.
*/
String toString();
/**
* Compares this duration with another.
*/
int compareTo(BaseDuration other);
/**
* Adds this duration to a date.
*/
Date plus(Date date);
/**
* Subtracts this duration from a date.
*/
Date minus(Date date);
/**
* Adds another duration to this one.
*/
BaseDuration plus(BaseDuration other);
/**
* Subtracts another duration from this one.
*/
BaseDuration minus(BaseDuration other);
/**
* Multiplies this duration by a factor.
*/
BaseDuration multiply(Number factor);
/**
* Divides this duration by a divisor.
*/
BaseDuration div(Number divisor);
/**
* Gets the negative of this duration.
*/
BaseDuration negative();
/**
* Gets the absolute value of this duration.
*/
BaseDuration abs();
}Represents a fixed duration of time (hours, minutes, seconds, milliseconds).
class TimeDuration extends BaseDuration {
/**
* Creates a TimeDuration with the specified components.
*/
TimeDuration(int days, int hours, int minutes, int seconds, int millis);
/**
* Creates a TimeDuration from milliseconds.
*/
TimeDuration(long millis);
/**
* Gets the number of days.
*/
int getDays();
/**
* Gets the number of hours (0-23).
*/
int getHours();
/**
* Gets the number of minutes (0-59).
*/
int getMinutes();
/**
* Gets the number of seconds (0-59).
*/
int getSeconds();
/**
* Gets the number of milliseconds (0-999).
*/
int getMillis();
/**
* Converts to total milliseconds.
*/
long toMilliseconds();
/**
* Converts to total seconds.
*/
long toSeconds();
/**
* Converts to total minutes.
*/
long toMinutes();
/**
* Converts to total hours.
*/
long toHours();
/**
* Converts to total days.
*/
long toDays();
/**
* Creates a string representation.
*/
String toString();
/**
* Adds this duration to a Calendar.
*/
void addToCalendar(Calendar calendar);
/**
* Subtracts this duration from a Calendar.
*/
void subtractFromCalendar(Calendar calendar);
}Represents durations that depend on calendar context (months, years).
class DatumDependentDuration extends BaseDuration {
/**
* Creates a DatumDependentDuration with the specified components.
*/
DatumDependentDuration(int years, int months, int days, int hours, int minutes, int seconds, int millis);
/**
* Gets the number of years.
*/
int getYears();
/**
* Gets the number of months.
*/
int getMonths();
/**
* Gets the number of days.
*/
int getDays();
/**
* Gets the number of hours.
*/
int getHours();
/**
* Gets the number of minutes.
*/
int getMinutes();
/**
* Gets the number of seconds.
*/
int getSeconds();
/**
* Gets the number of milliseconds.
*/
int getMillis();
/**
* Converts to milliseconds relative to a specific date.
*/
long toMilliseconds(Date relativeTo);
/**
* Creates a string representation.
*/
String toString();
/**
* Adds this duration to a Calendar.
*/
void addToCalendar(Calendar calendar);
/**
* Subtracts this duration from a Calendar.
*/
void subtractFromCalendar(Calendar calendar);
}Utility methods for creating common durations.
class Duration {
/**
* Creates a duration from milliseconds.
*/
static TimeDuration ofMilliseconds(long millis);
/**
* Creates a duration from seconds.
*/
static TimeDuration ofSeconds(long seconds);
/**
* Creates a duration from minutes.
*/
static TimeDuration ofMinutes(long minutes);
/**
* Creates a duration from hours.
*/
static TimeDuration ofHours(long hours);
/**
* Creates a duration from days.
*/
static TimeDuration ofDays(long days);
/**
* Creates a duration from weeks.
*/
static TimeDuration ofWeeks(long weeks);
/**
* Creates a datum dependent duration from months.
*/
static DatumDependentDuration ofMonths(int months);
/**
* Creates a datum dependent duration from years.
*/
static DatumDependentDuration ofYears(int years);
/**
* Parses a duration from a string.
*/
static BaseDuration parse(String durationString);
}import groovy.time.TimeCategory;
import java.util.Date;
import java.util.Calendar;
// Use TimeCategory for natural time arithmetic
Date now = new Date();
// Using TimeCategory.use() for safe category application
TimeCategory.use(new Closure<Void>(null) {
public Void doCall() {
// Add time periods
Date tomorrow = now.plus(1.getDays());
Date nextWeek = now.plus(1.getWeeks());
Date nextMonth = now.plus(1.getMonths());
Date nextYear = now.plus(1.getYears());
// Add multiple time units
Date future = now.plus(2.getHours()).plus(30.getMinutes());
// Subtract time periods
Date yesterday = now.minus(1.getDays());
Date lastWeek = now.minus(7.getDays());
// Calculate differences
BaseDuration diff = future.minus(now);
System.out.println("Now: " + now);
System.out.println("Tomorrow: " + tomorrow);
System.out.println("Future: " + future);
System.out.println("Difference: " + diff);
return null;
}
});import groovy.time.TimeDuration;
import groovy.time.DatumDependentDuration;
import groovy.time.Duration;
// Create specific durations
TimeDuration twoHoursThirtyMinutes = new TimeDuration(0, 2, 30, 0, 0);
TimeDuration fiveMinutes = Duration.ofMinutes(5);
DatumDependentDuration threeMonths = Duration.ofMonths(3);
// Duration arithmetic
TimeDuration combined = (TimeDuration) twoHoursThirtyMinutes.plus(fiveMinutes);
TimeDuration doubled = (TimeDuration) fiveMinutes.multiply(2);
TimeDuration half = (TimeDuration) twoHoursThirtyMinutes.div(2);
// Apply durations to dates
Date startDate = new Date();
Date endDate = combined.plus(startDate);
System.out.println("Start: " + startDate);
System.out.println("Duration: " + combined);
System.out.println("End: " + endDate);
// Convert durations
long totalMinutes = combined.toMinutes();
long totalSeconds = combined.toSeconds();
long totalMillis = combined.toMilliseconds();
System.out.println("Total minutes: " + totalMinutes);
System.out.println("Total seconds: " + totalSeconds);
System.out.println("Total milliseconds: " + totalMillis);import groovy.time.TimeCategory;
import java.util.Date;
import java.util.Calendar;
import java.text.SimpleDateFormat;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Calculate business days and complex periods
TimeCategory.use(new Closure<Void>(null) {
public Void doCall() {
Date projectStart = new Date();
// Project timeline calculations
Date milestone1 = projectStart.plus(2.getWeeks()).plus(3.getDays());
Date milestone2 = milestone1.plus(1.getMonths()).plus(1.getWeeks());
Date projectEnd = projectStart.plus(6.getMonths());
// Calculate working days (excluding weekends)
BaseDuration projectDuration = projectEnd.minus(projectStart);
System.out.println("Project Start: " + formatter.format(projectStart));
System.out.println("Milestone 1: " + formatter.format(milestone1));
System.out.println("Milestone 2: " + formatter.format(milestone2));
System.out.println("Project End: " + formatter.format(projectEnd));
System.out.println("Total Duration: " + projectDuration);
// Calculate age from birthdate
Calendar birthCal = Calendar.getInstance();
birthCal.set(1990, Calendar.MARCH, 15);
Date birthDate = birthCal.getTime();
BaseDuration age = new Date().minus(birthDate);
long ageInDays = age.toDays();
long ageInYears = ageInDays / 365;
System.out.println("Birth Date: " + formatter.format(birthDate));
System.out.println("Age in days: " + ageInDays);
System.out.println("Age in years: " + ageInYears);
return null;
}
});import groovy.time.TimeDuration;
import groovy.time.Duration;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
// Create various durations
List<TimeDuration> durations = new ArrayList<>();
durations.add(Duration.ofHours(2));
durations.add(Duration.ofMinutes(90));
durations.add(Duration.ofSeconds(7200));
durations.add(Duration.ofDays(1));
durations.add(new TimeDuration(0, 1, 30, 0, 0));
// Sort durations
Collections.sort(durations);
System.out.println("Sorted durations:");
for (TimeDuration duration : durations) {
System.out.println(duration + " (" + duration.toMinutes() + " minutes)");
}
// Find maximum and minimum durations
TimeDuration maxDuration = Collections.max(durations);
TimeDuration minDuration = Collections.min(durations);
System.out.println("Maximum duration: " + maxDuration);
System.out.println("Minimum duration: " + minDuration);
// Duration arithmetic
TimeDuration totalDuration = new TimeDuration(0, 0, 0, 0, 0);
for (TimeDuration duration : durations) {
totalDuration = (TimeDuration) totalDuration.plus(duration);
}
System.out.println("Total duration: " + totalDuration);
System.out.println("Average duration: " + totalDuration.div(durations.size()));import groovy.time.TimeCategory;
import groovy.time.DatumDependentDuration;
import java.util.Calendar;
import java.util.Date;
// Working with calendar-dependent durations
Calendar calendar = Calendar.getInstance();
calendar.set(2023, Calendar.JANUARY, 31);
Date endOfJanuary = calendar.getTime();
TimeCategory.use(new Closure<Void>(null) {
public Void doCall() {
// Adding months can have different results depending on the date
Date oneMonthLater = endOfJanuary.plus(1.getMonths());
Date twoMonthsLater = endOfJanuary.plus(2.getMonths());
// February has fewer days, so this will be February 28 (or 29)
System.out.println("End of January: " + endOfJanuary);
System.out.println("One month later: " + oneMonthLater);
System.out.println("Two months later: " + twoMonthsLater);
// Working with leap years
calendar.set(2024, Calendar.FEBRUARY, 29); // Leap year
Date leapDay = calendar.getTime();
Date nextYear = leapDay.plus(1.getYears());
System.out.println("Leap day 2024: " + leapDay);
System.out.println("Same day next year: " + nextYear); // Will be Feb 28, 2025
return null;
}
});import groovy.time.TimeCategory;
import groovy.time.TimeDuration;
import java.util.Calendar;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
// Working with different time zones
SimpleDateFormat utcFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
utcFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
SimpleDateFormat localFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
Calendar utcCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Date utcNow = utcCalendar.getTime();
TimeCategory.use(new Closure<Void>(null) {
public Void doCall() {
// Calculate meeting times across time zones
Date meetingTimeUTC = utcNow.plus(2.getHours());
// Convert to different time zones for display
System.out.println("Meeting time UTC: " + utcFormatter.format(meetingTimeUTC));
System.out.println("Meeting time local: " + localFormatter.format(meetingTimeUTC));
// Calculate duration until meeting
BaseDuration timeUntilMeeting = meetingTimeUTC.minus(utcNow);
System.out.println("Time until meeting: " + timeUntilMeeting);
// Schedule recurring meetings
List<Date> recurringMeetings = new ArrayList<>();
Date currentMeeting = meetingTimeUTC;
for (int i = 0; i < 5; i++) {
recurringMeetings.add(currentMeeting);
currentMeeting = currentMeeting.plus(1.getWeeks());
}
System.out.println("Recurring meetings:");
for (Date meeting : recurringMeetings) {
System.out.println(" " + utcFormatter.format(meeting));
}
return null;
}
});Install with Tessl CLI
npx tessl i tessl/maven-org-codehaus-groovy--groovy