Monday, July 18, 2022

How to format/parse dates with LocalDateTime in Java 8 - Example Tutorial

One of the common tasks in a Java project is formatting or parsing data to String and vice-versa. Parsing date means you have a String that represents a date like "2017-08-3" and you want to convert it into an object which represents the date in Java, like java.util.Date in pre-Java 8 world and LocalDate or LocalDatetime in Java 8 world. Similarly, formatting a date means converting a date instance into a String, for example, you have a Date object or LocalDatetime object and you want a String in dd-MM-yyyy format. Java 8 API provides good support for formatting and parsing dates.

For example, if you have a date as String like "2017-08-3 12:30" and you want to convert it to a LocalDateTime instance, which is a new class from JDK 8 Date and Time API and contains both date and time part, how do you do that? Well, you can use the format() and parse() method from the LocalDateTime class to achieve that, but you need one more thing, a date format.

Prior to Java 8, you might be aware that we use SimpleDateFormat and DateFormat class to represent a format, which has lots of problems like they were heavy, mutable, and not thread-safe, which means you cannot share them and every time you need to convert String to Date, you have to create a new DateFormat object. 

Though encapsulating SimpleDateFormat into a thread-local variable does offer some respite, it wasn't enough.

JDK 8 has addressed this problem in the new DateTimeFormatter class, which can be used to define the date and time format like "yyyy-MM-dd HH: mm", the syntax to specify the format is the same as what we used earlier with SimpleDateFormat class, but this class is both thread-safe and immutable which means you can share its instance between threads. Ideally, you can store the reference of DateTimeFormatter into a static variable to make it global.



Another advantage of using DateTimeFormatter is that it offers several built-in formatter like java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME, which can represent a date as "2017-08-03T10:15:30". You can see a full list of built-in formatter in Javadoc.

Once you got your formatter, parsing or formatting the date is as easy as calling a method. You just need to call the LocalDateTime.parse() method to convert a String to LocalDateTime in Java 8. The parse() takes a String and parse into the LocalDateTime instance based upon the format specified by DateTimeFormatter.

The parse() method is also overloaded and by default, it uses ISO_LOCAL_DATE_TIME format which is "yyyy-MM-dd HH:mm" i.e. "2017-08-03T10:15:30", but if your String is in a different format then you can specify a separate formatter.

Btw, if you are not familiar with new Java 8 features like this one then I suggest you first go through comprehensive and up-to-date Java 8 courses. If you need recommendations you can check these best Java 8 programming courses from Udemy and Coursera. It's also very affordable and you can buy in just $10 on Udemy sales which happen every now and then.

So, enough theory, let's begin the real work...




How to format dates with LocalDateTime? Example

Let's assume you are loading the date as String from a database or a file that is in ISO format e.g. "yyyy-MM-dd HH:mm" and you want to convert them to java.time.LocalDateTime. Here are the exact steps to parse a date String to LocalDateTime in Java 8:

1) Create a DateTimeFormatter object

2) Use LocalDateTime.parse(string, formatter) method to convert String to LocalDatetime object

Btw, in our case dates are ISO format, you don't need to create a separate formatter and you can directly call the parse method, as shown in the following example:

String date = "2017-03-08T12:30:54";
LocalDateTime localdatetime = LocalDateTime.parse(date);

System.out.println("origional date as string: " + date);
System.out.println("generated LocalDateTime: " + localdatetime);

Output
origional date as string: 2017-03-08T12:30:54
generated LocalDateTime: 2017-03-08T12:30:54

Btw, if your date string is not in ISO format expected by parse method e.g. there is no T or missing minute of the second part then it will throw DateTimeParseException.


For example, if you want to parse "2017-08-3 12:30" or "2017-03-08 12:30:54", then it will throw the following exception:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2017-03-08T12:30:54' could not be parsed at index 10 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851) at java.time.LocalDateTime.parse(LocalDateTime.java:492) at Demo.main(Demo.java:22)



To avoid this error, you can create a DateTimeFormatter instance that matches your date String. For example, if your dates are similar to "2017-08-3 12:30" then you can create a DateTimeFormatter as shown below:

DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");

After this, you can use this formatter instance to parse String to LocalDateTime as shown in the following example:

String date = "2017-03-08 12:30:54";
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(date, format);

System.out.println("origional date as string: " + date);
System.out.println("generated LocalDateTime: " + dateTime);

Output:
origional date as string: 2017-03-08 12:30
generated LocalDateTime: 2017-03-08T12:30

You can see there is no more exception, but you must ensure that the date as String must match with the pattern you define in your DateTimeFormatter instance. 

Since it is also thread-safe and immutable, you can even store this in a static variable and share it among other parts of your program. You can read more about thread safety and immutability in the new date and time API on Java SE 8 for the Really Impatient book





How to format dates with LocalDateTime in Java?

In the last section, you have learned how to parse a date e.g. convert a String representation of a date into the corresponding object i.e. LocalDateTime in Java 8. Now, let's do the opposite, create a formatted string from an existing LocalDateTime object e.g. a date in "dd-MM-yyyy" format.

Again we need a DateTimeFormatter instance that holds our date pattern and then we can use the format() method of the LocalDateTime class to achieve this. 

Though, you should remember that format() is a non-static method and you need an instance of LocalDateTime class to call this method. Here is an example to format dates with LocalDatetime in Java 8:

DateTimeFormatter aFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
LocalDateTime localDateTime = LocalDateTime.of(2017, Month.AUGUST, 3, 12, 30);
String foramttedString = localDateTime.format(aFormatter); // "2017-03-08 12:30"

System.out.println("origional LocalDatetime object: " + localDateTime);
System.out.println("generated string : " + foramttedString);

Output:
origional LocalDatetime object: 2017-08-03T12:30
generated string : 03-08-2017 12:30

You should notice that we are calling the format method on an object and not with a class because it's a non-static method which is just the opposite of parse(), which is a static method. You can also see that generated String confirms your pattern i.e. "03-08-2017 12:30" is in "dd-MM-yyyy HH:mm" format.




Java Program to format/parse Date with LocalDateTime in JDK 8

This is our sample Java program which encapsulates examples of both parsing and formatting dates with LocalDateTime in Java 8.

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

/*
* Java Program to parse to LocalDateTime in JDK 8. 
* We'll convert a String "2017-03-08 12:30" into LocalDateTime.
* we'll also see how to format a LocalDateTime instance to String format. 
*/
public class Demo {

public static void main(String[] args) throws Exception {

// parsing a string date to LocalDateTime
String date = "2017-03-08 12:30";
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(date, format);

System.out.println("origional date as string: " + date);
System.out.println("generated LocalDateTime: " + dateTime);


//formatting a LocalDateTime to string instance
DateTimeFormatter aFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime localDateTime = LocalDateTime.of(2017, Month.AUGUST, 3, 12, 30);
String foramttedString = localDateTime.format(aFormatter); // "2017-03-08 12:30"

System.out.println("origional LocalDatetime object: " + localDateTime);
System.out.println("generated string : " + foramttedString);

// be careful, string must contain date and time portion
// if you are converting to LocalDateTime, or else, your
// code will break

LocalDateTime dateWithoutTime = LocalDateTime.parse("2017-08-03", format);
}

}

Output
origional date as string: 2017-03-08 12:30
generated LocalDateTime: 2017-03-08T12:30
origional LocalDatetime object: 2017-08-03T12:30
generated string : 2017-08-03 12:30
Exception in thread "main" java.time.format.DateTimeParseException: 
Text '2017-08-03' could not be parsed at index 10
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at Demo.main(Demo.java:35)





Important points

1) The LocalDateTime.parse() method is used for parsing and it's a static method but the format() method is not static and it needs a LocalDateTime instance to call upon. That's the important difference to notice between the parse() and format() method. For example LocalDateTime.format(DateTimeFromatter) is illegal in Java and give compile-time error.

2) You must make sure that your String confirms the format you are using for both parsing and formatting if it doesn't then both parse() and format() method will throw DateTimeParseException e.g. "Exception in thread "main" java.time.format.DateTimeParseException: Text '2017-08-03' could not be parsed at index 10".

3) There are several built-in formats available in Java 8, our same should be to leverage if it severs the purpose instead of creating a new one.

4) Since DateTimeFormatter is both immutable and thread-safe, it's recommended to store it in a static variable and share it among whoever wants to use it but make sure that the variable is both static and final so that the thread can read it but cannot assign a new reference or null to it, which can cause subtle issues. 

You can further see my post about the dangers of using a static variable in a multi-threading environment for more details.

Here is the summary of code to format or parse date to LocalDateTime in Java 8:

How to format/parse dates with LocalDateTime in Java 8 - Example Tutorial


That's all about how to format and parse dates with LocalDateTime in Java 8. As I said, each of the new classes like LocalDate, LocalTime, and LocalDateTime has a parse and format method which can be used to convert a string to date and vice-versa. 

Just remember that you need a DateTimeFormatter, whose pattern must match with your date String, if it doesn't then both parse() methods will throw java.time.format.DateTimeParseException error.

You should also remember the difference between the parse() and format() method, the former is static while the latter is non-static. Another thing you can keep in mind is to reuse the DateTimeFormatter instance either in form of a static variable or leveraging several built-in formatter available in JDK. You can further read Java SE 8 for Really Impatient to learn more about new features of Java 8, including the new Date and Time API.


Other Java 8 Date and Time tutorials you may like to explore:
  • The Java Developer RoadMap (roadmap)
  • How to compare two dates in Java? (tutorial)
  • Top 5 Websites to learn Java coding for FREE (websites)
  • How to get the current Timestamp value in Java? (tutorial)
  • 10 Tools Every Java Programmer should learn (tools)
  • How to convert String to LocalDateTime in Java 8? (example)
  • How to convert java.util.Date to java.sql.Timestamp in JDBC? (tutorial)
  • How to convert Date to LocalDateTime in Java 8? (tutorial)
  • How to get the current date and time in Java 6? (tutorial)
  • How to parse String to Date using the JodaTime library? (example)
  • How to convert java.util.Date to java.sql.Date in JDBC? (tutorial)
  • How to convert String to LocalDateTime in Java 8 (tutorial)

Thanks for reading this article so far. If you like this Java 8 date and time tutorial and my tips then please share them with your friends and colleagues. If you have any questions or feedback then please drop a comment.

2 comments:

  1. I'm getting an exception for this:
    String date = "2017-03-08 12:30:54";
    DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    LocalDateTime dateTime = LocalDateTime.parse(date, format);
    Exception:
    Exception in thread "main" java.time.format.DateTimeParseException: Text '2017-03-08 12:30:54' could not be parsed, unparsed text found at index 16
    Looks like pattern and date should exactly match:
    - "2017-03-08 12:30:54" for "yyyy-MM-dd HH:mm:ss"
    - "2017-03-08 12:30" for "yyyy-MM-dd HH:mm"
    BTW:
    java.text.SimpleDateFormat works for this case.

    ReplyDelete
  2. @Anonymous, yes, pattern must match exactly with the date, otherwise you will receive, java.time.format.DateTimeParseException. Btw, thanks for sharing your input about SimpleDateFormat, didn't know that it handle this case :-)

    ReplyDelete