61

I have a problem. I am trying to convert some strings to date, and I don't know the format the date is arriving.

It might come as yyyy.mm.dd hh:mm:ss or MM.dd.yy hh:mm:ss and so on.

How can I convert these strings to Date? I tried this:

DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
Date d = (Date)formatter.parse(someDate);

But when I printed out someDate it printed out like this: 2010-08-05 12:42:48.638 CEST which means yyyy.mm.dd hh:mm:ss, however when I ran the above code, the date object now became Sat Jan 31 00:42:48 CET 11 which is strange to say the least.

Any ideas how I can correctly format strings to date?

Colin Hebert
  • 91,525
  • 15
  • 160
  • 151
Shervin Asgari
  • 23,901
  • 30
  • 103
  • 143

11 Answers11

57

You cant!

If you have the date 2010-08-05 then it can be either 5th August 2010, or 8th May 2010 - you need to know the date format (or at least prioritise one format over the over) to tell them apart.

Justin
  • 84,773
  • 49
  • 224
  • 367
  • 6
    Well, technically he can, but he won't know if it is correct. – Paweł Dyda Sep 14 '10 at 09:24
  • 41
    @Pawel: "technically I can represent Pi exactly with an integer value, it just won't be correct." – Joachim Sauer Sep 14 '10 at 09:42
  • 4
    @JoachimSauer: technically you can't ever represent Pi exactly with any number of digits ;) – code0100fun Jun 21 '13 at 18:29
  • 1
    This may be obvious, but depending on use-case, it may be enough to just flag ambiguous date formats and move on. – josiah Oct 09 '14 at 16:05
  • 3
    @mahieddine Actually it is an answer. It says you can't! – Shervin Asgari Oct 27 '15 at 12:07
  • Well, you can make a very good guess. I wrote such project and it worked well. You can see my own answer to this question. But here is a link to an article that describes my proposed solution: https://www.linkedin.com/pulse/java-8-javatime-package-parsing-any-string-date-michael-gantman/ – Michael Gantman Jun 12 '19 at 13:45
  • @ShervinAsgari Is it possible to get a date without knowing the time format(12/24). I'm able to get the date format and the date time string. Is it possible to get the Date in the date time string? – Santhosh Sep 02 '19 at 10:11
13

I agree with Kragen that in the general case there is no correct solution. However, if the following conditions hold, you may use the solution below:

  1. You have a set of all possible formats

  2. There is no ambiguity between the formats; no date expression can be successfully parsed by two of them.

Consider the following solution which iterates over a list of possible formats. This solution makes use of ThreadLocal, in order to make date parsing efficient in a multi-threaded environment (remember that SimpleDateFormat isn't thread safe):

public class FlexibleDateParser {
    private List<ThreadLocal<SimpleDateFormat>> threadLocals = new  ArrayList<ThreadLocal<SimpleDateFormat>>();

    public FlexibleDateParser(List<String> formats, final TimeZone tz){
        threadLocals.clear();
        for (final String format : formats) {
            ThreadLocal<SimpleDateFormat> dateFormatTL = new ThreadLocal<SimpleDateFormat>() {
                protected SimpleDateFormat initialValue() {
                    SimpleDateFormat sdf = new SimpleDateFormat(format);
                    sdf.setTimeZone(tz); 
                    sdf.setLenient(false);
                    return sdf;
                }
            };
            threadLocals.add(dateFormatTL);
        }       
    }

    public Date parseDate(String dateStr) throws ParseException {
        for (ThreadLocal<SimpleDateFormat> tl : threadLocals) {
            SimpleDateFormat sdf = tl.get();
            try {
                return sdf.parse(dateStr);
            } catch (ParseException e) {
                // Ignore and try next date parser
            }
        }
        // All parsers failed
        return null;
    }       
}
Eyal Schneider
  • 22,166
  • 5
  • 47
  • 78
  • It appears that the thread safety of this example is only needed if a single instance of FlexibleDateParser could be used by multiple threads (such as existing as a static or singleton, or dependency injection in thread creation). Correct? – Nicole Sep 18 '12 at 22:13
  • NickC, I believe thats correct. Still creating a lot of simpledateformats per request might be affecting performance since it is an expensive operation. – Sebastian Aug 12 '15 at 14:24
10

As noted before, you need to at least have an ordered list of pattern candidates. Once you have that, Apache DateUtils has a parseDate(String dateString, String[] patterns) method that lets you easily try out a list of patterns on your date string, and parse it by the first one that matches:

public static Date parseDate(String str,
                         String[] parsePatterns)
                  throws ParseException

Parses a string representing a date by trying a variety of different parsers.

The parse will try each parse pattern in turn. A parse is only deemed successful if it parses the whole of the input string. If no parse patterns match, a ParseException is thrown.

The parser will be lenient toward the parsed date.

Amber
  • 2,413
  • 1
  • 15
  • 20
oksayt
  • 4,333
  • 1
  • 22
  • 41
7

Here is a quick and dirty solution based on american date formats.

public Date parseDate(String strDate) throws Exception
{
    if (strDate != null && !strDate.isEmpty())
    {
        SimpleDateFormat[] formats =
                new SimpleDateFormat[] {new SimpleDateFormat("MM-dd-yyyy"), new SimpleDateFormat("yyyyMMdd"),
                        new SimpleDateFormat("MM/dd/yyyy")};

        Date parsedDate = null;

        for (int i = 0; i < formats.length; i++)
        {
            try
            {
                parsedDate = formats[i].parse(strDate);
                return parsedDate;
            }
            catch (ParseException e)
            {
                continue;
            }
        }
    }
    throw new Exception("Unknown date format: '" + strDate + "'");
}
bjoern
  • 1,009
  • 3
  • 15
  • 31
3

Your problem is related to Internationalization. As Kragen answered, you can't just parse date in unknown format. Although you can scan all possible locales and parse something, but you wouldn't know if it was parsed correctly.

Just a little i18n background:

Q: Can you tell me what day month and year this date is referring to:

09/11/10?

A: Without knowing the locale, you can't. It could be anything. September, 11th in USA. November, 9th in Great Britain. And so on.

Paweł Dyda
  • 18,366
  • 7
  • 57
  • 79
2

I once had a task to write a code that would parse a String to date where date format was not known in advance. I.e. I had to parse any valid date format. I wrote a project and after that I wrote an article that described the idea behind my implementation. Here is the link to the article: Java 8 java.time package: parsing any string to date. General Idea is to write all the patterns that you wish to support into external properties file and read them from there and try to parse your String by those formats one by one until you succeed or run out of formats. Note that the order also would be important as some Strings may be valid for several formats (US/European differences). Advantage is that you can keep adding/removing formats to the file without changing your code. So such project could also be customized for different customers

Dharman
  • 30,962
  • 25
  • 85
  • 135
Michael Gantman
  • 7,315
  • 2
  • 19
  • 36
1

If its a protocol; define the format - perhaps ISO that will irritate everyone except us in sweden...

If it input from users; Let them set their locale. If you can do it, show the parsed date in full format so the user can verify it, like November 10, 2009.

KarlP
  • 5,149
  • 2
  • 28
  • 41
0

My only guess, is that you should gather some statistics first to figure out the format.

If you're lucky, you will have date like "2010/08/13" which can be parsed unambiguously

Alexander Malakhov
  • 3,383
  • 2
  • 33
  • 58
0

I created a utility that tries to parse some common date formats. It tries to check which digit is greater than 12 or else if both are less than 12 it prefers user-defined boolean preferMonthFirst based on which it will choose between MM/dd/yyyy or dd/MM/yyyy

It also accepts prefer24HourTime boolean for parsing time.

I did not use the list and iterate over it and try to parse it and tried to catch exception because Exceptions are costlier. So based on separator and length I tried to find the date format.

You can find usages in the test cases.

https://github.com/rajat-g/DateParser

Rajat Gupta
  • 3
  • 1
  • 7
-1

Here is the simple solution, which is worked for me. This is the simple method to parse the date, pass the String as argument & parse it in any format those you want.

String dateToConvert(String date) {
        String strDate = "";
        try {
            //create SimpleDateFormat object with source string date format
            DateFormat sdfSource = new SimpleDateFormat("yyyy-MM-dd");

            //parse the string into Date object
            Date d = sdfSource.parse(date);
            LocalLog.e(LOG_TAG, d.toString());
            //create SimpleDateFormat object with desired date format
            SimpleDateFormat sdfDestination = new SimpleDateFormat(AppConstants.UNIVERSAL_DATE_FORMAT);

            //parse the date into another format
            strDate = sdfDestination.format(d);

            LocalLog.e(LOG_TAG, strDate);

        } catch (ParseException pe) {
            System.out.println("Parse Exception : " + pe);
        }
        return strDate;
    }
Maheshwar Ligade
  • 6,709
  • 4
  • 42
  • 59
-3
public  String compareDate( PaymentTxnRequest request ) throws ParseException { 
        Date debitDate= request.getPaymentTxn().getCrValDt();
        Date now = new Date();
        String response="";
        SimpleDateFormat sdfDate = new SimpleDateFormat("dd/MM/yyyy");
        String strCurrDate = sdfDate.format(now);
        String strDebitDate = sdfDate.format(debitDate);
        System.out.println("Current Date: " + strCurrDate);
        Date currentDate =  new SimpleDateFormat("dd/MM/yyyy").parse(strCurrDate);
        Date txnDate =  new SimpleDateFormat("dd/MM/yyyy").parse(strDebitDate);
        System.out.println("C -> "+currentDate);
        System.out.println("C -> "+txnDate); 
         if (txnDate!=null){
         if (currentDate.equals(txnDate))
         {
             System.out.println("Valid Txn");
             response="valid";
         }
         if (currentDate.after(txnDate))
         {
            System.out.println("--> Not  Valid TXN Past");   
            response="notValid";
         }
        if (currentDateenter code here.before(txnDate)){
            System.out.println("Future Valid TXn");
             response="future";
        }
     }
        return response;
    }