Backend Development 9 min read

Java Date and Time Formatting: Methods, Thread Safety, and Best Practices

This article compares three common Java date‑time formatting approaches—SimpleDateFormat, Java 8’s DateTimeFormatter, and Apache Commons Lang3’s DateFormatUtils—examines thread‑safety issues, provides code examples, and offers conversion utilities between Date, LocalDate, and LocalDateTime for robust backend development.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Java Date and Time Formatting: Methods, Thread Safety, and Best Practices

Introduction

In everyday project development, formatting dates and times is a frequent requirement. While many consider it trivial, the choice of formatting method can greatly affect performance and thread safety.

Common Date Formatting Methods

The article presents three typical ways to format dates in Java:

Using SimpleDateFormat (pre‑Java 8)

Using Java 8's DateTimeFormatter

Using Apache Commons Lang3's DateFormatUtils

Analysis of SimpleDateFormat

Method One: Beginner Implementation

Many beginners use SimpleDateFormat , which is not thread‑safe because it shares a mutable Calendar instance across threads.

Reason

In a multithreaded environment, concurrent calls to SimpleDateFormat.format() modify the shared calendar, leading to incorrect results.

public static void main(String[] args) {
    Date now = new Date();
    String strDateFormat = "yyyy-MM-dd HH:mm:ss";
    // Beginner implementation
    SimpleDateFormat f = new SimpleDateFormat(strDateFormat);
    System.out.println("SimpleDateFormat:" + f.format(now));
    // Java 8 implementation
    LocalDateTime localDateTime = now.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    String result = localDateTime.format(DateTimeFormatter.ofPattern(strDateFormat));
    System.out.println("DateTimeFormatter:" + result);
    // Commons‑Lang3 implementation
    result = DateFormatUtils.format(now, strDateFormat);
    System.out.println("DateFormatUtils:" + result);
}

A multithreaded test demonstrates occasional false outputs, confirming thread‑unsafe behavior.

public class SimpleDateFormatTest {
    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES,
        new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));
    public void test() {
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

Solutions

Declare SimpleDateFormat as a local variable.

Synchronize access with a lock, e.g., synchronized(lock) .

Use ThreadLocal to give each thread its own SimpleDateFormat instance.

Method Two: Java 8 Advanced Implementation

Java 8 recommends DateTimeFormatter , which is thread‑safe and offers a rich API.

LocalDateTime localDateTime = LocalDateTime.now();
String result = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("DateTimeFormatter:" + result);

The new API works with LocalDate (date only) and LocalDateTime (date and time).

Method Three: Commons‑Lang3 Implementation

Apache Commons Lang3 provides DateFormatUtils , which internally uses FastDateFormat for efficient, thread‑safe formatting.

Date now = new Date();
String strDateFormat = "yyyy-MM-dd HH:mm:ss";
String result = DateFormatUtils.format(now, strDateFormat);
System.out.println("DateFormatUtils:" + result);

Dependency:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.11</version>
</dependency>

Conversion Utilities Between Date, LocalDate, and LocalDateTime

public static LocalDate date2LocalDate(Date date) {
    if (date == null) return null;
    return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}

public static LocalDateTime date2LocalDateTime(Date date) {
    if (date == null) return null;
    return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
}

public static Date localDate2Date(LocalDate localDate) {
    if (localDate == null) return null;
    ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault());
    return Date.from(zonedDateTime.toInstant());
}

public static Date localDateTime2Date(LocalDateTime localDateTime) {
    return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}

Summary

The article introduces three common Java date‑formatting techniques, highlights the thread‑unsafe nature of SimpleDateFormat , recommends the thread‑safe DateTimeFormatter for Java 8+, and suggests using Apache Commons Lang3's DateFormatUtils for a simple, efficient, and memory‑friendly solution, optionally extending it in a custom DateUtils class.

backendJavaDateTimesimpledateformatApache CommonsDateTimeFormatterThreadSafety
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.