Date and time manipulation is a common task in software development, from logging events to scheduling tasks and handling user input. In Java, working with dates and times can be verbose and error-prone, especially with the older Date
and Calendar
classes. Fortunately, Groovy simplifies these operations with its syntactic sugar, operator overloading, and enhanced time APIs.
Groovy builds on top of Java's time libraries and adds expressive utilities such as TimeCategory
, better formatting support, and seamless interoperability with Java 8's java.time.*
classes. This tutorial will guide you through various ways to work with dates and times in Groovy—from basic creation to advanced manipulation—along with practical examples you can apply in real-world applications.
Whether you're building a Grails application or using Groovy for scripting, this guide will help you manage time the Groovy way.
1. Working with Date
and Calendar
Groovy enhances Java's Date
and Calendar
classes with simplified syntax and helpful methods. Let’s explore the basics:
1.1 Creating a Date
You can create a date instance easily using Groovy:
def now = new Date()
println "Current date and time: $now"
1.2 Formatting a Date
Formatting dates in Groovy can be done with SimpleDateFormat
, just like in Java:
def sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
println "Formatted date: ${sdf.format(now)}"
1.3 Using Calendar
Calendar
is useful when you need to manipulate specific fields such as year, month, or day:
def calendar = Calendar.instance
calendar.set(2025, Calendar.JUNE, 24)
def customDate = calendar.time
println "Custom date: $customDate"
1.4 Getting Parts of a Date
You can extract parts of a date like this:
println "Year: ${calendar.get(Calendar.YEAR)}"
println "Month: ${calendar.get(Calendar.MONTH) + 1}" // Months are 0-based
println "Day: ${calendar.get(Calendar.DAY_OF_MONTH)}"
📝 Tip: Even though
Date
andCalendar
are considered legacy in modern Java, Groovy still supports them and adds intuitive enhancements, making them easier to use.
2. Using TimeCategory
Natural Syntax
One of Groovy’s most elegant features for working with dates and times is the TimeCategory
class. It allows you to write natural, human-readable expressions for time calculations such as adding or subtracting durations.
To use TimeCategory
, you wrap your logic in a use(TimeCategory)
block.
2.1 Adding Time Units
import groovy.time.TimeCategory
use(TimeCategory) {
def now = new Date()
def future = now + 3.days + 5.hours
println "Now: $now"
println "Future: $future"
}
In this example:
-
3.days
and5.hours
are Groovy's syntactic sugar for time durations. -
You can use
+
and-
operators to manipulateDate
objects.
2.2 Subtracting Time Units
use(TimeCategory) {
def now = new Date()
def past = now - 10.minutes
println "10 minutes ago: $past"
}
2.3 Time Durations and Intervals
Groovy also provides a TimeDuration
class under the hood when using TimeCategory
. You can store and reuse these durations.
use(TimeCategory) {
def duration = 2.days + 4.hours
def start = new Date()
def end = start + duration
println "Start: $start"
println "End: $end"
println "Duration: $duration"
}
This can be particularly useful for scheduling and time-range calculations.
2.4 Difference Between Two Dates
use(TimeCategory) {
def d1 = new Date()
sleep(1500) // Sleep for 1.5 seconds
def d2 = new Date()
def diff = d2 - d1
println "Difference: $diff" // prints TimeDuration (e.g., 0 days, 0 hours, 0 minutes, 1 second, 500 milliseconds)
}
💡 Note:
TimeCategory
only affects code within its scope. This avoids polluting the global namespace, keeping your scripts clean.
3. Groovy with Java 8+ Time API
While Groovy adds many enhancements to Java's legacy Date
and Calendar
, it also works seamlessly with the modern Java 8+ Date-Time API introduced in java.time
. This API is immutable, thread-safe, and highly recommended for new applications.
Groovy’s syntax sugar makes working with classes like LocalDate
, LocalDateTime
, and ZonedDateTime
even more concise and readable.
3.1 Working with LocalDate
and LocalDateTime
import java.time.LocalDate
import java.time.LocalDateTime
def today = LocalDate.now()
def currentDateTime = LocalDateTime.now()
println "Today's date: $today"
println "Current date and time: $currentDateTime"
3.2 Creating Specific Dates and Times
def birthday = LocalDate.of(1990, 5, 20)
def appointment = LocalDateTime.of(2025, 6, 24, 15, 30)
println "Birthday: $birthday"
println "Appointment: $appointment"
3.3 Parsing and Formatting Dates
Use DateTimeFormatter
to parse and format these modern date-time types.
import java.time.format.DateTimeFormatter
def formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm")
def dateTime = LocalDateTime.parse("24-06-2025 15:30", formatter)
println "Parsed date: $dateTime"
println "Formatted: ${dateTime.format(formatter)}"
3.4 Date Arithmetic
def nextWeek = today.plusWeeks(1)
def lastMonth = today.minusMonths(1)
println "Next week: $nextWeek"
println "Last month: $lastMonth"
3.5 Comparing Dates
def launchDate = LocalDate.of(2025, 7, 1)
println "Is today before launch? ${today.isBefore(launchDate)}"
println "Is today after launch? ${today.isAfter(launchDate)}"
📝 Tip:
java.time.*
types are preferred for persistence and APIs due to immutability and better time zone support.
4. Date Formatting and Parsing
Formatting and parsing date-time values is essential for displaying dates to users, storing them in readable formats, or converting strings into date-time objects. Groovy supports both the traditional SimpleDateFormat
and the modern DateTimeFormatter
from Java 8.
4.1 Using SimpleDateFormat
(Legacy)
import java.text.SimpleDateFormat
def date = new Date()
def sdf = new SimpleDateFormat("EEEE, MMMM d, yyyy HH:mm:ss")
println "Formatted date: ${sdf.format(date)}"
def parsedDate = sdf.parse("Tuesday, June 24, 2025 14:00:00")
println "Parsed date: $parsedDate"
Common SimpleDateFormat
patterns:
-
yyyy-MM-dd
– 2025-06-24 -
dd/MM/yyyy
– 24/06/2025 -
EEEE
– Day of the week (e.g., Tuesday)
4.2 Using DateTimeFormatter
(Modern Java 8+)
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
def dateTime = LocalDateTime.now()
def formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
println "Formatted LocalDateTime: ${dateTime.format(formatter)}"
Parsing a string back to LocalDateTime
:
def parsedDateTime = LocalDateTime.parse("2025-06-24 14:30:00", formatter)
println "Parsed LocalDateTime: $parsedDateTime"
4.3 Custom Formats for Different Locales
You can also apply locale-specific formatting using DateTimeFormatter
:
import java.time.format.FormatStyle
import java.util.Locale
import java.time.LocalDate
def localeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(Locale.FRANCE)
println "French date: ${LocalDate.now().format(localeFormatter)}"
💡 Best Practice: Prefer
DateTimeFormatter
for new projects—it’s thread-safe and more flexible thanSimpleDateFormat
.
5. Handling Time Zones
Time zones can be particularly challenging, especially when developing global applications. Java 8’s ZonedDateTime
and ZoneId
make working with time zones much more intuitive—and Groovy works seamlessly with them.
5.1 Getting the System Default Zone
import java.time.ZoneId
def defaultZone = ZoneId.systemDefault()
println "System default time zone: $defaultZone"
5.2 Creating a ZonedDateTime
import java.time.ZonedDateTime
import java.time.LocalDateTime
def localDateTime = LocalDateTime.of(2025, 6, 24, 10, 0)
def jakartaZone = ZoneId.of("Asia/Jakarta")
def zonedDateTime = ZonedDateTime.of(localDateTime, jakartaZone)
println "Jakarta time: $zonedDateTime"
5.3 Converting Between Time Zones
You can easily convert between zones using withZoneSameInstant()
:
def newYorkZone = ZoneId.of("America/New_York")
def newYorkTime = zonedDateTime.withZoneSameInstant(newYorkZone)
println "Converted to New York time: $newYorkTime"
5.4 Listing All Available Zone IDs
def zoneIds = ZoneId.availableZoneIds().sort()
println "Available zones (first 5):"
zoneIds.take(5).each { println it }
🕓 Tip: Always store date-time values in UTC (or use
ZonedDateTime
) when saving to a database or sending via APIs to avoid time zone inconsistencies.
6. Practical Examples
This section demonstrates how to apply Groovy’s date and time capabilities in useful scenarios you’re likely to encounter in web apps, reports, scheduling, and more.
6.1 Calculating Age from Birthdate
import java.time.LocalDate
import java.time.Period
def birthDate = LocalDate.of(1990, 6, 24)
def today = LocalDate.now()
def age = Period.between(birthDate, today).years
println "Age is: $age years"
🎂 Use case: Useful for profile systems, healthcare apps, or calculating experience duration.
6.2 Generating Timestamp-Based Filenames
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
def now = LocalDateTime.now()
def formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")
def filename = "backup_${now.format(formatter)}.zip"
println "Generated filename: $filename"
🗃️ Use case: Backup tools, exports, or logging systems that require unique, time-based names.
6.3 Getting the Start and End of the Week
import java.time.temporal.TemporalAdjusters
import java.time.DayOfWeek
def today = LocalDate.now()
def startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
def endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY))
println "Start of this week: $startOfWeek"
println "End of this week: $endOfWeek"
📆 Use case: Weekly reports, scheduling features, or calendar components.
6.4 Measuring Time Elapsed
import java.time.Duration
import java.time.Instant
def start = Instant.now()
sleep(1200) // simulate some work
def end = Instant.now()
def elapsed = Duration.between(start, end)
println "Elapsed time: ${elapsed.toMillis()} milliseconds"
⏱️ Use case: Performance benchmarking, profiling scripts, or tracking task execution time.
7. Conclusion
Groovy provides a rich and expressive toolkit for working with dates and times, making it far more pleasant than dealing with raw Java APIs alone. Whether you're using Groovy in scripts, test automation, or full-stack Grails applications, its enhancements save time and reduce boilerplate.
To summarize:
-
Use
Date
andCalendar
when working with legacy systems, but take advantage of Groovy’s syntactic sugar to simplify operations. -
Leverage
TimeCategory
for intuitive date math (2.days.from.now
) and easy interval handling. -
Prefer Java 8’s
java.time.*
classes (LocalDate
,LocalDateTime
,ZonedDateTime
) for modern, immutable, and thread-safe date-time management. -
Use
DateTimeFormatter
instead ofSimpleDateFormat
for safer, cleaner formatting. -
Always be mindful of time zones in global applications, and use
ZonedDateTime
andZoneId
to ensure accuracy.
By mastering both Groovy’s features and Java's modern time APIs, you’ll be equipped to handle any date/time scenario efficiently and clearly.
You can get the full source code on our GitHub.
That's just the basics. If you need more deep learning about Groovy and Grails, you can take the following cheap course:
- Mastering Grails. A Comprehensive Grails Course.
- Groovy Scripting for Developers / Testers
- Introduction to JVM Languages Clojure, Kotlin, and Groovy
Thanks!