How to Format Dates with DateTimeFormatter

In Java (starting from Java 8), you can format dates using the DateTimeFormatter class, which provides an easier and more modern approach to date and time formatting. This class is part of the java.time.format package and works seamlessly with the java.time API (e.g., LocalDate, LocalDateTime, ZonedDateTime).

Here’s how you can format dates with DateTimeFormatter:


Example of Formatting Dates with DateTimeFormatter

package org.kodejava.datetime;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatterExample {
   public static void main(String[] args) {
      // Get the current date and time
      LocalDateTime currentDateTime = LocalDateTime.now();

      // Define a formatter with a custom pattern
      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");

      // Format the date and time
      String formattedDateTime = currentDateTime.format(formatter);

      // Print the result
      System.out.println("Formatted Date and Time: " + formattedDateTime);
   }
}

Example Output:

Formatted Date and Time: 02/08/2025 15:52:30

Common Patterns for Date and Time

Here are the most commonly used symbols for formatting patterns with DateTimeFormatter:

Symbol Meaning Example
y Year 2025
M Month 08 or August
d Day of the month 02
E Day name in a week Tue
H Hour (0-23) 15
h Hour (1-12, AM/PM) 3
m Minute in hour 45
s Second in minute 30
a AM/PM PM
z Time zone name PDT
'text' Literal text ‘at’

Example with Fully Custom Pattern

package org.kodejava.datetime;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class CustomDateFormatting {
   public static void main(String[] args) {
      LocalDate currentDate = LocalDate.now();

      // Custom date format
      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE, MMMM dd yyyy");
      String formattedDate = currentDate.format(formatter);

      // Print the formatted date
      System.out.println("Custom Formatted Date: " + formattedDate);
   }
}

Output:

Custom Formatted Date: Saturday, August 02 2025

Predefined Formatters in DateTimeFormatter

DateTimeFormatter also provides several predefined, common formatters:

Formatter Pattern Example
DateTimeFormatter.ISO_DATE yyyy-MM-dd 2025-08-02
DateTimeFormatter.ISO_TIME HH:mm:ss 15:45:30
DateTimeFormatter.ISO_DATE_TIME yyyy-MM-dd'T'HH:mm:ss 2025-08-02T15:45:30
DateTimeFormatter.BASIC_ISO_DATE yyyyMMdd 20250802
DateTimeFormatter.RFC_1123_DATE_TIME RFC 1123 format Sat, 02 Aug 2025 15:45:30

Example: Formatting Dates with Time Zones

package org.kodejava.datetime;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class ZonedDateTimeFormatterExample {
   public static void main(String[] args) {
      // Get the current date and time with time zone
      ZonedDateTime zonedDateTime = ZonedDateTime.now();

      // Define a formatter with a custom pattern that includes the time zone
      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss z Z");

      // Format the ZonedDateTime
      String formattedDateTime = zonedDateTime.format(formatter);

      // Print the result
      System.out.println("Formatted Date and Time with Time Zone: " + formattedDateTime);
   }
}

Output:

Formatted Date and Time with Time Zone: 02/08/2025 15:50:30 PDT -0700

Explanation of Pattern:

  • z: Displays the short name of the time zone (e.g., PDT, GMT).
  • Z: Displays the time zone offset (e.g., -0700).

Parsing and Formatting Specific Time Zones

You can work with specific time zones using the ZoneId class:

package org.kodejava.datetime;

import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class SpecificTimeZoneExample {
   public static void main(String[] args) {
      // Get the current date and time in a specific time zone
      ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Europe/London"));

      // Define a formatter
      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE, MMM dd yyyy HH:mm:ss z");

      // Format the ZonedDateTime
      String formattedDateTime = zonedDateTime.format(formatter);

      // Print the result
      System.out.println("London Time: " + formattedDateTime);
   }
}

Output:

London Time: Saturday, Aug 02 2025 23:50:30 BST

Predefined Formatter for Time Zones

If you’d like to use the predefined formatters to format dates with time zones, you can try:

package org.kodejava.datetime;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class PredefinedTimeZoneFormatter {
   public static void main(String[] args) {
      // Get the current ZonedDateTime
      ZonedDateTime zonedDateTime = ZonedDateTime.now();

      // Use a predefined formatter
      String formattedDateTime = zonedDateTime.format(DateTimeFormatter.RFC_1123_DATE_TIME);

      // Print the result
      System.out.println("Formatted with Predefined Formatter: " + formattedDateTime);
   }
}

Output:

Formatted with Predefined Formatter: Sat, 02 Aug 2025 15:50:30 -0700

Notes:

  1. Thread Safety:
    Unlike SimpleDateFormat, DateTimeFormatter is thread-safe and can be safely used in concurrent environments.

  2. Extensible Patterns:
    You can use literal text in the patterns by enclosing it in single quotes ('text').

  3. Working with Time Zones:
    If you are working with time zones, you can use ZonedDateTime or OffsetDateTime along with a DateTimeFormatter.

  4. ZoneId Use:
    You can specify almost any valid time zone using ZoneId.of("Zone_Name"). Example: "America/New_York", "Asia/Tokyo", "Australia/Sydney".

  5. Daylight Saving Time:
    Time zones take daylight saving time into account automatically if applicable.
  6. Predefined Formatters with Zones:
    Predefined formatters like DateTimeFormatter.ISO_ZONED_DATE_TIME and DateTimeFormatter.RFC_1123_DATE_TIME are handy for common time zone formats.

How to Format Dates with SimpleDateFormat

You can format dates in Java using the SimpleDateFormat class, which is part of the java.text package. This class allows you to specify patterns describing the formatting and parsing of date and time objects.

Here’s a step-by-step guide:


Example of Formatting Dates with SimpleDateFormat

package org.kodejava.text;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatExample {
   public static void main(String[] args) {
      // Create an instance of SimpleDateFormat
      // Specify the desired pattern
      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");

      // Get the current date
      Date currentDate = new Date();

      // Format the date
      String formattedDate = simpleDateFormat.format(currentDate);

      // Print the result
      System.out.println("Formatted Date: " + formattedDate);
   }
}

Common Patterns for Date and Time Formatting

Here are some of the most commonly used patterns:

Symbol Description Example
y Year 2025
M Month in year 08 or August
d Day of the month 02
h Hour in AM/PM (1-12) 3
H Hour in day (0-23) 15
m Minute in hour 45
s Second in minute 30
S Millisecond 978
E Day of the week Tue
D Day of the year 214
z Time zone PDT
Z Time zone offset -0700

You can mix and match these symbols to create a pattern that suits your needs.


Example with Custom Pattern

SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMM dd, yyyy hh:mm a");
Date date = new Date();
System.out.println("Custom Formatted Date: " + dateFormat.format(date));

Output:

Custom Formatted Date: Saturday, Aug 02, 2025 10:30 AM

Notes:

  1. Thread Safety:
    SimpleDateFormat is not thread-safe. If you need to use it in a multithreaded environment, you should manage synchronization or use java.time.format.DateTimeFormatter, which is thread-safe and introduced in Java 8.

  2. For Java 8 and Later:
    If you’re using Java 8 or later, consider using the new java.time API (DateTimeFormatter) for better clarity and thread safety.

How to Use Locale for Internationalization

Internationalization (i18n) involves designing applications so that they can be adapted to different languages, regions, and cultures. In Java, the Locale class is a fundamental part of i18n. It represents a specific geographical, political, or cultural region and is used in conjunction with various APIs to format dates, numbers, and text according to a specific locale.

Here are the basic steps to use Locale for internationalization:


1. Creating a Locale

You can create a Locale object in a few different ways:

package org.kodejava.util;

import java.util.Locale;

public class LocaleExample {
    public static void main(String[] args) {
        // Using predefined constants
        Locale defaultLocale = Locale.getDefault();  // System default locale
        Locale usLocale = Locale.US;                 // United States

        // Using constructors
        Locale customLocale = new Locale("fr", "FR");  // French (France)

        // Using Locale.Builder (for more control)
        Locale builderLocale = new Locale.Builder()
                .setLanguage("de")  // German
                .setRegion("DE")    // Germany
                .build();

        System.out.println("Default Locale: " + defaultLocale);
        System.out.println("US Locale: " + usLocale);
        System.out.println("Custom Locale: " + customLocale);
        System.out.println("Builder Locale: " + builderLocale);
    }
}

2. Using Locale with Date/Time Formatting

Locale is commonly used to format dates and times in a way that is familiar to a specific region:

package org.kodejava.util;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

public class DateLocalizationExample {
    public static void main(String[] args) {
        Date now = new Date();

        // Formatting date in French (France)
        Locale frenchLocale = new Locale("fr", "FR");
        DateFormat frenchDateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, frenchLocale);
        System.out.println("Date in French: " + frenchDateFormatter.format(now));

        // Formatting date in German (Germany)
        Locale germanLocale = new Locale("de", "DE");
        DateFormat germanDateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, germanLocale);
        System.out.println("Date in German: " + germanDateFormatter.format(now));
    }
}

3. Using Locale with Numbers and Currency Formatting

The NumberFormat class allows you to format numbers and currencies according to a locale:

package org.kodejava.util;

import java.text.NumberFormat;
import java.util.Locale;

public class NumberLocalizationExample {
    public static void main(String[] args) {
        double amount = 12345.67;

        // Format currency in US locale
        Locale usLocale = Locale.US;
        NumberFormat usFormatter = NumberFormat.getCurrencyInstance(usLocale);
        System.out.println("In US: " + usFormatter.format(amount));

        // Format currency in Japanese locale
        Locale japanLocale = Locale.JAPAN;
        NumberFormat japanFormatter = NumberFormat.getCurrencyInstance(japanLocale);
        System.out.println("In Japan: " + japanFormatter.format(amount));
    }
}

4. Internationalizing Messages with ResourceBundles

For text and messages, Java provides the ResourceBundle class, which allows you to store localized strings in property files.

  1. Create Properties Files (e.g., messages_en_US.properties, messages_fr_FR.properties):
    # messages_en_US.properties
    greeting=Hello
    farewell=Goodbye
    
    # messages_fr_FR.properties
    greeting=Bonjour
    farewell=Au revoir
    
  2. Read ResourceBundle Based on Locale:
    package org.kodejava.util;
    
    import java.util.Locale;
    import java.util.ResourceBundle;
    
    public class ResourceBundleExample {
      public static void main(String[] args) {
         // Locale for English (US)
         Locale usLocale = new Locale("en", "US");
         ResourceBundle bundleUS = ResourceBundle.getBundle("messages", usLocale);
         System.out.println("US Greeting: " + bundleUS.getString("greeting"));
         System.out.println("US Farewell: " + bundleUS.getString("farewell"));
    
         // Locale for French (France)
         Locale frLocale = new Locale("fr", "FR");
         ResourceBundle bundleFR = ResourceBundle.getBundle("messages", frLocale);
         System.out.println("French Greeting: " + bundleFR.getString("greeting"));
         System.out.println("French Farewell: " + bundleFR.getString("farewell"));
      }
    }
    

5. Switching Locales Dynamically

You can dynamically switch between different locales at runtime, based on user preferences or system settings:

package org.kodejava.util;

import java.util.Locale;

public class LocaleSwitcher {
   public static void setLocale(String language, String country) {
      Locale.setDefault(new Locale(language, country));
   }

   public static void main(String[] args) {
      // Default locale
      System.out.println("Default Locale: " + Locale.getDefault());

      // Switch to French
      setLocale("fr", "FR");
      System.out.println("Current Locale: " + Locale.getDefault());
      // Perform locale-specific operations...

      // Switch back to English
      setLocale("en", "US");
      System.out.println("Current Locale: " + Locale.getDefault());
      // Perform locale-specific operations...
   }
}

Key Points:

  1. The Locale object is essential for tailoring applications for specific languages and regions.
  2. Utilize DateFormat, NumberFormat, and ResourceBundle for locale-based formatting and localized messages.
  3. Keep localized data (like messages) in separate resource files (.properties) to facilitate easier translation.
  4. Avoid hardcoding language-specific content directly in the code—this ensures maintainability and scalability.

How to Use System.currentTimeMillis() for Performance Timing

In Java, System.currentTimeMillis() is commonly used as a simple way to measure the execution time of a block of code or a specific operation in terms of milliseconds. Here’s how you can effectively use it for performance timing:

Example Usage

package org.kodejava.lang;

public class PerformanceTimingExample {
    public static void main(String[] args) {
        // Record the start time
        long startTime = System.currentTimeMillis();

        // The code you want to measure
        performOperation();

        // Record the end time
        long endTime = System.currentTimeMillis();

        // Calculate the elapsed time
        long elapsedTime = endTime - startTime;

        // Print the result
        System.out.println("Execution time: " + elapsedTime + " milliseconds");
    }

    private static void performOperation() {
        try {
            // Simulate time-consuming task
            Thread.sleep(2000); // Sleep for 2 seconds
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Steps Explained

  1. Record Start Time: Use System.currentTimeMillis() before the block of code you want to measure.
  2. Execute Operation: Run the code or process whose performance you need to measure.
  3. Record End Time: Capture the time after the code execution using System.currentTimeMillis().
  4. Calculate Elapsed Time: Subtract the start time from the end time to get the elapsed time in milliseconds.
  5. Output Results: Display or log the elapsed time for performance analysis.

Things to Keep in Mind

  • Resolution: System.currentTimeMillis() measures the current time in milliseconds since the Unix epoch (January 1, 1970). However, its granularity may vary depending on the system, and it is not as precise as System.nanoTime() for very fine-grained measurements.
  • Avoid Garbage Collection Interference: When measuring performance, ensure that garbage collection has minimal impact by warming up the JVM and avoiding memory-intensive operations.
  • Use System.nanoTime() for Better Precision: If you need higher precision or want to avoid timer granularity issues, consider using System.nanoTime() instead. This measures elapsed time in nanoseconds and is suitable for shorter durations.

Example with System.nanoTime()

package org.kodejava.lang;

public class NanoTimingExample {
    public static void main(String[] args) {
        // Record the start time
        long startTime = System.nanoTime();

        // The code you want to measure
        performOperation();

        // Record the end time
        long endTime = System.nanoTime();

        // Calculate the elapsed time in milliseconds
        long elapsedTime = (endTime - startTime) / 1_000_000;

        // Print the result
        System.out.println("Execution time: " + elapsedTime + " milliseconds");
    }

    private static void performOperation() {
        try {
            // Simulate time-consuming task
            Thread.sleep(2000); // Sleep for 2 seconds
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Conclusion

System.currentTimeMillis() is a simple and effective method to time operations, especially those involving multiple seconds or milliseconds. However, for finer-grained timing or benchmarking (e.g., sub-millisecond accuracy), prefer System.nanoTime(). Always ensure that your measurements are consistent and unaffected by other system activities, such as garbage collection or OS-level processes.

How to Use Objects.requireNonNull() Effectively

The Objects.requireNonNull() method is a utility provided in Java to enforce that an object is not null during runtime. It is part of the java.util.Objects class starting from Java 7 and is commonly used for validating method parameters, ensuring that null values don’t propagate and cause unexpected NullPointerExceptions later.

Here’s a detailed explanation of how to use Objects.requireNonNull() effectively:


What It Does

Objects.requireNonNull() checks whether the provided reference is null. If it is null, it throws a NullPointerException. Optionally, you can provide a custom message to make the exception more meaningful.


Methods Available

There are three main variants of Objects.requireNonNull():

  1. public static <T> T requireNonNull(T obj)
    • Throws NullPointerException if obj is null.
  2. public static <T> T requireNonNull(T obj, String message)
    • Throws NullPointerException with the provided message if obj is null.
  3. public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) (Java 8 or later)
    • Defers the creation of the message via the Supplier, which is a performance-friendly option since the message is only computed if obj is null.

When to Use It

  1. To Validate Parameters
    Use Objects.requireNonNull() at the beginning of a method to validate parameters and catch null values early.

    public void setName(String name) {
       this.name = Objects.requireNonNull(name, "Name cannot be null!");
    }
    
  2. Before Using a Field in Code
    Validate fields that are expected to be non-null before operating on them.

    public void processData(Data data) {
       Objects.requireNonNull(data, "Data must not be null before processing.");
       // process the data
    }
    
  3. Constructor Argument Validation
    When writing constructors, validate inputs immediately to ensure that your object is consistently in a valid state.

    public Example(String id) {
       this.id = Objects.requireNonNull(id, "ID must not be null.");
    }
    
  4. To Prevent Nullable Logic Elsewhere in Code
    By enforcing non-null guarantees in one place (e.g., via method validation), null checks do not need to be repeated elsewhere in the codebase.


Best Practices

  1. Always Provide a Meaningful Message
    The message should indicate what went wrong, so developers can quickly pinpoint the issue.

    public void processFile(File file) {
       Objects.requireNonNull(file, "File parameter is required.");
    }
    
  2. Use a Supplier When the Message Is Expensive to Build
    If creating the message involves non-trivial operations, use the Supplier<String> version to only compute the message when it’s actually necessary:

    public void process(String input) {
       Objects.requireNonNull(input, () -> "Input cannot be null at " + LocalDateTime.now());
    }
    
  3. Avoid Overusing It
    Don’t use Objects.requireNonNull() unnecessarily, such as in places where null values are either acceptable or already handled by the program.

    // Not recommended - Avoid redundant requireNonNull()
    public String getNonNullValue(String value) {
       return Objects.requireNonNull(value, "Param cannot be null.");
    }
    
    // Instead, handle null where needed
    return (value == null) ? "Default" : value;
    
  4. In Lombok Constructors
    If using Lombok, you can reduce boilerplate code by annotating with @NonNull in the parameters, and Lombok will handle the validation using Objects.requireNonNull() under the hood.

    @Data
    public class Example {
       private final @NonNull String name;
    }
    
  5. Avoid Overhead
    Don’t use Objects.requireNonNull() in performance-critical sections of the code. For repetitive checks in such cases, consider earlier null validations.


Example

Here’s a complete example of how Objects.requireNonNull() works in practice:

package org.kodejava.util;

import java.util.Objects;

public class User {
    private final String username;

    public User(String username) {
        // Validate that the username is not null
        this.username = Objects.requireNonNull(username, "Username cannot be null.");
    }

    public void updateEmail(String email) {
        Objects.requireNonNull(email, "Email cannot be null.");
        System.out.println("Email updated to: " + email);
    }

    public String getUsername() {
        return username;
    }

    public static void main(String[] args) {
        try {
            User user = new User(null); // Throws NullPointerException with message
        } catch (NullPointerException e) {
            System.out.println(e.getMessage()); // Output: "Username cannot be null."
        }

        User user = new User("JohnDoe");

        try {
            user.updateEmail(null); // Throws NullPointerException with message
        } catch (NullPointerException e) {
            System.out.println(e.getMessage()); // Output: "Email cannot be null."
        }
    }
}

Advantages

  • Improved Readability: Instead of writing verbose null-checks, Objects.requireNonNull() provides clear intent with less code.
  • Centralized Null Handling: Enforces null-checking policy consistently.
  • Clear Debugging: The custom exception message pinpoints the issue.

Conclusion

Objects.requireNonNull() is a highly effective tool to enforce non-null constraints in your code. When combined with thoughtful custom messages or suppliers, it helps you write cleaner, safer, and more readable Java code.