Tuesday, July 13, 2021

How to convert String to Date in Java? Multithreading Example

Data type conversion is one of the most common tasks in programming and a good programmer must know how to convert one type to another type. There are many times when you will be required to convert a String to java.util.Date object mostly in a different format like dd-MM-yy or yyyy-MM-dd or simply yyyy MM dd. For example, clients pass dates as String to Server or sometimes we read Date related data from CSV file. Java provides API for parsing String to date using DateFormat class, though Java's Date and Time API is severely criticized, it is also the most used Date and Time format solution. 

Though Joda Date and Time API is always there and now Java 8 also got a new date and time API, many of us don't have the luxury to use it in production. Many enterprise application is still running on Java 5 and Java 6, forget about JDK 7 or 8.

It may take another 5 years before you start using lambda expressions and Stream API. Apart from several design mistakes of Date class e.g. mutability, not intuitive, the most obvious problem with formatting date in Java is SimpleDateFormat not being thread-safe.

That's why I wrote this post to show how to convert String to Date correctly in a Java multi-threading environment. There are mainly two ways to use SimpleDateFormat properly in a concurrent application, either synchronize the access to DateFormat object or use ThreadLocal variable.

Earlier we have seen how to make SimpleDateFormat thread-safe, and in this tutorial, we will learn how to use the synchronized keyword for making DateFormat thread-safe.





String to Date Example in Java Multithreading

Here is a complete example of converting String to Date on a Multi-threading Java Program. In this program, I have created a nested static class DateUtils, which holds a static final DateFormat object. 

Here we have created a method called toDate() which returns a date object when we pass String to it. This DateFormat object is created with the pattern "dd/MM/yyy", you can choose any format as you like, please refer to the cheat sheet given in this blog post to see various date formatting options.

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

/**
* String to Date Example in Java Multi-threading. In single threaded environment
 * converting String to date is easy because you can use SimpleDateFormat
* class without worrying. * * @author Javin Paul */ public class DateToStringDemo{ public static class DateUtils { private static final DateFormat format = new SimpleDateFormat("dd/MM/yyyy"); public static synchronized Date toDate(String date) throws ParseException { return format.parse(date); } } public static void main(String args[]) { // One example, long dates String string = "February 6, 2014"; Date date; try { date = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH).parse(string); System.out.println(date); // Thu Feb 06 00:00:00 IST 2014 } catch (ParseException e) { e.printStackTrace(); } // Another example, date in MM/dd/yyyy format String source = "01/09/2014"; try { date = new SimpleDateFormat("MM/dd/yyyy").parse(source); System.out.println(date); // Thu Jan 09 00:00:00 IST 2014 } catch (ParseException e) { e.printStackTrace(); } // One more String to Date Example in 04 JAN 2014 20:30 String birthday = "04 JAN 2014 20:30"; try { date = new SimpleDateFormat("dd MMM yyyy HH:mm").parse(birthday); System.out.println(date); // Sat Jan 04 20:30:00 IST 2014 } catch (ParseException e) { e.printStackTrace(); } // Remember "m" and "M" are different String weddingday = "30-01-2011"; try { date = new SimpleDateFormat("dd-mm-yyyy").parse(weddingday); System.out.println(date); //Sun Jan 30 00:01:00 IST 2011 } catch (ParseException e) { e.printStackTrace(); } // calling synchronized method to convert String to date try { Date mydate = DateUtils.toDate("04/02/2014"); System.out.println(mydate); //Tue Feb 04 00:00:00 IST 2014 } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Output: Thu Feb 06 00:00:00 IST 2014 Thu Jan 09 00:00:00 IST 2014 Sat Jan 04 20:30:00 IST 2014 Sun Jan 30 00:01:00 IST 2011 Tue Feb 04 00:00:00 IST 2014


As I said, In multithreading, your String to date conversion logic must be thread-safe. You have two options, you can either synchronize it or use SimpleDateFormat as a thread-local variable. Here is what  we have done by using synchronized keywords:

public static class DateUtils {

        private static final DateFormat format 
                       = new SimpleDateFormat("dd/MM/yyyy");

        public static synchronized Date toDate(String date) 
                     throws ParseException {
            return format.parse(date);
        }
 }

When you pass an incorrect date to this toDate() method, you will see the following Exception in your console.

// spelling mistake, should be February instead of Febrauary, see additional 'a'
java.text.ParseException: Unparseable date: "Febrauary 6, 2014"
   at java.text.DateFormat.parse(DateFormat.java:357)
   at Testing.main(Testing.java:19)



Here is another common example of passing an incorrect date String that is not in the expected format and cannot be converted to date.

// the date passed was "02/06/2014", which was not in expected format 
// e.g. dd MMM yyyy HH:mm, should
// be 04 JAN 2014 20:30
java.text.ParseException: Unparseable date: "02/06/2014"
               at java.text.DateFormat.parse(DateFormat.java:357)
               at Testing.main(Testing.java:39)


Date to String Conversion - Things to remember

HEre are important notes about converting a Date to String in Java, every Java programmer should know about these things and keep these in mind:

1. Long time ago, one developer asked me, instead of compromising performance by using synchronized block, why not just use DateFormat as a local variable. An important point to note is that creating DateFormat every time is very expensive, it will be overkill for an application that needs to convert millions of String to Date like an Order processing engine, which receives millions of orders.


2. Sharing the same DateFormat object between multiple threads can also result in unexpected results. For example, you may end up with a completely different date than the expected value, or you will be greeted by java.lang.ArrayIndexOutOfBoundsException or java.lang.NumberFormatException.


3. There are multiple ways to correctly use DateFormat in multi-threading Java applications, which includes using SimpleDateFormat as ThreadLocal or properly synchronizing excess to DateFormat object. I suggest using the ThreadLocal approach for core Java applications, which is not running on the managed environment like a Web server or application server. 

Since managed environment manages essential services on behalf of applications e.g. Thread pools, there is a chance that their thread outlived the application itself, which can create ThreadLocal memory leaks in Java.



4. Another mistake to avoid while using DateFormat in a multithreading environment is, storing them in a static variable. When a programmer comes to know that creating DateFormat locally, every time you need to convert String to Date is very time and memory consuming, they usually create a global DateFormat object and store them as static variables. 

Though that solves their memory and performance issue, by only using one instance of DateFormat object, it introduces more subtle concurrency bugs in the application, due to shared and unprotected access of a non-thread object. Remember, DateFormat is not threadsafe in Java.


How to convert String to Date in Java? Multithreading Example



5.  Apart from the thread-safety issue, the String to date conversion process also faces subtle mistakes on formatting options, mainly due to a lack of good knowledge of formatting characters explained in Javadoc. 

Remember even the same character in different cases results in a totally different meaning, which may result in "java.text.ParseException: Unparseable date" error. One of those is using "m" as Month, which actually stands for minutes. 

For example following date format "dd-mm-yyyy" is incorrect and will result in different meanings, when passed a date like 30-01-2011 e.g. 01 will be minutes.

6) Have various Date format options handy, a cheat sheet image is not bad at all.
G   Era designator       Text               AD
y   Year                 Year               1996; 96
M   Month in year        Month              July; Jul; 07
w   Week in year         Number             27
W   Week in month        Number             2
D   Day in year          Number             189
d   Day in month         Number             10
F   Day of week in month Number             2
E   Day in week          Text               Tuesday; Tue
u   Day number of week   Number             1
a   Am/pm marker         Text               PM
H   Hour in day (0-23)   Number             0
k   Hour in day (1-24)   Number             24
K   Hour in am/pm (0-11) Number             0
h   Hour in am/pm (1-12) Number             12
m   Minute in hour       Number             30
s   Second in minute     Number             55
S   Millisecond          Number             978
z   Time zone            General time zone  Pacific Standard Time; PST; GMT-08:00
Z   Time zone            RFC 822 time zone  -0800
X   Time zone            ISO 8601 time zone -08; -0800; -08:00


That's all on how to convert String to Date in Java. As I said parsing String to Date in Java is very simple in single threaded program,  but you have to be careful while doing this in multi-threaded Java program because SimpleDateFormat is not thread-safe. You can use any of synchronization mechanism we have discussed in this article, but prefer the ThreadLocal variable over synchronized keyword because of performance reason. Cost of synchronization is much higher than maintaining separate copy for each thread. Another thing you should remember while creating Date from String is to remember various Date format options e.g. many people make mistake between 'm' and 'M', where m is for minute of hour while M is for month of year. By the if you are using Java 8, consider using new Date and Time API for converting String to Date. There is no point using old date API if you already moved to JDK 8.


7 comments:

  1. just like d is for date and D is for Day, Similaryl m is for "minute in a hour" and M is for "month in a year", good source of silly mistakes

    ReplyDelete
  2. Interresting usage of the synchronised keyword but you are bottlenecked in the case of severall threads.

    Have you ever consider using the DateTimeFormat adhoc replacement in org.joda.time library ?

    ReplyDelete
    Replies
    1. I think that's a very good option if you don't mind an additional step from converting to joda time LocalDate to java.util.Date. It's thread-safe and easy to use.

      Delete
  3. > How to convert String to Date Example in Java Multithreading ?
    pre java 8 : the safer, simpler option is joda time.

    ReplyDelete
    Replies
    1. In fact for all your date needs one should use joda time, not just formatting e.g. their LocalDate class is better than Date and Joda also has useful Instant, Duration classes to calcualte differnece between two dates e.g. how many days to Christmas, or New Year. On Java 8, yes, you can use java Date and time API but before that, go for Joda.

      Delete
  4. ThreadLocal for your SimpleDateFormat is a better solution.

    ReplyDelete
    Replies
    1. Indeed, and I think that is the most legitimate and natural use of ThreadLocal variable in Java. SimpleDateFormat + ThreadLocal rocks!!

      Delete

Feel free to comment, ask questions if you have any doubt.