Working with Dates and Times in Groovy: A Practical Guide

by Didin J. on Jun 24, 2025 Working with Dates and Times in Groovy: A Practical Guide

Master date and time manipulation in Groovy with this comprehensive guide. Learn about Date, Calendar, TimeCategory, LocalDateTime, and formatting examples.

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 and Calendar are considered legacy in modern Java, Groovy still supports them and adds intuitive enhancements, making them easier to use.

Working with Dates and Times in Groovy: A Practical Guide - example 1


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 and 5.hours are Groovy's syntactic sugar for time durations.

  • You can use + and - operators to manipulate Date 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.

Working with Dates and Times in Groovy: A Practical Guide - example 2


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.

Working with Dates and Times in Groovy: A Practical Guide - example 3


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 than SimpleDateFormat.

Working with Dates and Times in Groovy: A Practical Guide - example 4


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.

Working with Dates and Times in Groovy: A Practical Guide - example 5


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.

Working with Dates and Times in Groovy: A Practical Guide - example 6

Working with Dates and Times in Groovy: A Practical Guide - example 7

Working with Dates and Times in Groovy: A Practical Guide - example 8

Working with Dates and Times in Groovy: A Practical Guide - example 9


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 and Calendar 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 of SimpleDateFormat for safer, cleaner formatting.

  • Always be mindful of time zones in global applications, and use ZonedDateTime and ZoneId 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:

Thanks!