1

String is an input and i have to check whether it is in any of these formats. If it is in sdf1 format then pass, if it is in sdf2,3.... then i add the missing the format and parse it with sdf1 format

SimpleDateFormat sdf1 = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss a");
SimpleDateFormat sdf2 = new SimpleDateFormat("MM/dd/yyyy hh:mm:ssa");
SimpleDateFormat sdf3 = new SimpleDateFormat("MM/dd/yyyy hh:mm a");
SimpleDateFormat sdf3 = new SimpleDateFormat("MM/dd/yyyy hh:mma");
SimpleDateFormat sdf3 = new SimpleDateFormat("MM/dd/yyyy hh:mm");
SimpleDateFormat sdf3 = new SimpleDateFormat("MM/dd/yyyy"); 

Here is what i have

try{
            // Parse if it is MM/dd/yyyy hh:mm:ss a
            cal = Calendar.getInstance();
            cal.setTime(sdf1.parse(inStr));
          }catch(Exception exp){
             try{
               cal = Calendar.getInstance();
               //Parse if it is MM/dd/yyyy hh:mm:ssa                      
               sdf2.parse(inStr);                 
               inStr = new StringBuilder(inStr).insert(str.length()-2, " ").toString();
               cal.setTime(sdf1.parse(inStr));
              }catch(Exception dte2){
                try{
                    cal = Calendar.getInstance();
                    //Parse if it is MM/dd/yyyy hh:mma
                    sdf3.parse(inStr);
                    //if pass then set 00:00:00 AM
                    inStr = inStr+" AM";
                    inStr = new StringBuilder(inStr).insert(str.length()-2, ":00").toString();
                    cal.setTime(sdf1.parse(inStr));

it keeps going like try parsing and if failed in exception block check for the next one. Is there any easy way to do this, may be in JAVA 8? i could use the link from marked as duplicate it talks about how to parse, but i have additional requirement as auto complete with missing format.

Pat
  • 535
  • 1
  • 16
  • 41
  • 1
    Possible duplicate of [How to parse dates in multiple formats using SimpleDateFormat](https://stackoverflow.com/questions/4024544/how-to-parse-dates-in-multiple-formats-using-simpledateformat) – Stefan Freitag Apr 05 '18 at 18:19

1 Answers1

5

In Java 8, you can use a java.time.format.DateTimeFormatter with lots of optional sections - delimited by []:

DateTimeFormatter fmt = DateTimeFormatter
    .ofPattern("MM/dd/yyyy[ hh:mm[[:ss][ ]a]][HH:mm]", Locale.US);

If you want just to validate the inputs - and don't assign the result to a date/time variable - just calling parse is enough. This works for all the cases below:

fmt.parse("10/20/2018");
fmt.parse("10/20/2018 10:20");
fmt.parse("10/20/2018 10:20AM");
fmt.parse("10/20/2018 10:20 AM");
fmt.parse("10/20/2018 10:20:30AM");
fmt.parse("10/20/2018 10:20:30 AM");

If the input is invalid, it'll throw a DateTimeParseException.

Note that there's a difference between HH and hh. H is for hour-of-day (values between 0 and 23), while h is for clock-hour-of-am-pm (values between 1 and 12). H can't be with AM/PM designator, while h must have AM/PM to disambiguate it. That's why there are 2 different optional sections, one with each field.

I also use Locale.US because AM/PM strings are localized. Although for most locales, the result is "AM" or "PM", for some others it can be lowercase ("am"), or some other values (午後 in Japanese, for example).

If you don't set the locale, it uses the JVM default. But I prefer to set it a specific one that I know it'll work with the inputs I have.


To print those values again, with all the fields, it's a little bit tricky. The inputs can have only a date (day, month, year), or a date and time (day, month, year, hour, minute), so one alternative is to use a LocalDateTime (and set the missing fields when not present).

You can use parseBest and then check the type that was parsed:

// parse, try to create a LocalDateTime - if it's not possible, try a LocalDate
TemporalAccessor parsed = fmt.parseBest("10/20/2018", LocalDateTime::from, LocalDate::from);
LocalDateTime dt = null;
if (parsed instanceof LocalDateTime) {
    // LocalDateTime parsed (all fields set)
    dt = (LocalDateTime) parsed;
} else if (parsed instanceof LocalDate) {
    // LocalDate parsed (set time fields)
    dt = ((LocalDate) parsed)
        // set time (use whatever value you want - I'm using 10 AM as example)
        .atTime(LocalTime.of(10, 0));
}

Then you use another formatter for output - that's because the first formatter will print all the optional sections when formatting, so it'll print the hours twice. Just create another one and that's it:

DateTimeFormatter outputFmt = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss a", Locale.US);
System.out.println(dt.format(outputFmt)); // 10/20/2018 10:00:00 AM
q112
  • 125
  • 3
  • thank you it works perfectly for validation, but i have to get in format "MM/dd/yyyy hh:mm:ss a" by adding the missing format values. Thats where i stuck. – Pat Apr 05 '18 at 18:37
  • Do you want to print the parsed values? Maybe you could create another formatter, using `DateTimeFormatterBuilder` and `parseDefaulting` - make a search and you'll find lots of examples – q112 Apr 05 '18 at 19:11
  • @Pat I've updated the answer. Not sure if that's what you need, though – q112 Apr 05 '18 at 19:34
  • Thank you, works perfectly and this what i need. Simple and perfect solution. I tried with all possible values and here is the format i used, to support single digit hour minute and i change the input string to uppercase always to support AM and am. – Pat Apr 05 '18 at 20:51
  • my latest format `DateTimeFormatter fmt = DateTimeFormatter.ofPattern("M/d/[uuuu][uu][ h:m[[:ss][ ]a]][ H:m[:ss]]", Locale.US);` but this format isnt works for `02/03/2018 05:00:00`, what is that missing in my format – Pat Apr 10 '18 at 17:20