4

I am trying to write a DateTimeFormatter that will allow me to take in multiple different string formats, and then convert the string formats to a specific type. Due to the scope of the project and the code that already exists, I cannot use a different type of formatter.

eg. I want to accept MM/dd/yyyy as well as yyyy-MM-dd'T'HH:mm:ss but then convert both to MM/dd/yyyy.

Could someone suggest ideas on how to do this with the org.joda.time.format?

I have not found a good/working example of this online.

AlexK
  • 336
  • 8
  • 21
  • 41
  • 1
    I'm not sure what the difference is but I want to convert a String to a DateTime, ie., DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("MM/dd/yyyy"); – AlexK Jun 14 '17 at 18:35

2 Answers2

4

I'm using Joda-Time 2.9.7 and JDK 1.7.0_79.

You can use DateTimeFormatterBuilder.append method: it receives a printer (with a pattern used to print date/times) and an array of parsers with all possible input patterns:

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.DateTimeParser;

// MM/dd/yyyy format
DateTimeFormatter monthDayYear = DateTimeFormat.forPattern("MM/dd/yyyy");
// array of parsers, with all possible input patterns
DateTimeParser[] parsers = {
    // parser for MM/dd/yyyy format
    monthDayYear.getParser(),
    // parser for yyyy-MM-dd'T'HH:mm:ss format
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").getParser() };
DateTimeFormatter parser = new DateTimeFormatterBuilder()
    // use the monthDayYear formatter for output (monthDayYear.getPrinter()) and parsers array for input (parsers)
    .append(monthDayYear.getPrinter(), parsers)
    // create formatter (using UTC to avoid DST problems)
    .toFormatter().withZone(DateTimeZone.UTC);

// test with MM/dd/yyyy
DateTime datetime1 = parser.parseDateTime("06/14/2017");
System.out.println(parser.print(datetime1)); // 06/14/2017

// test with yyyy-MM-dd'T'HH:mm:ss
DateTime datetime2 = parser.parseDateTime("2017-06-14T10:30:40");
System.out.println(parser.print(datetime2)); // 06/14/2017

I've used DateTimeZone.UTC to avoid problems with Daylight Saving Time.

For example, in my default timezone (America/Sao_Paulo), last year (2016), DST started at October 16th: at midnight, the clocks move 1 hour ahead (so, tecnically, midnight doesn't exist at this day, because time changes from 23:59:59 to 01:00:00).

The problem is, when parsing MM/dd/yyyy format, there's no fields for hour, minute or second, and the parser sets 0 as a default value for all these fields (so the hour becomes midnight). But if I try to parse in a date where DST starts (like 10/16/2016) and don't use the UTC as above, the code throws an exception because midnight doesn't exist in that day (due to DST hour shift).

Using UTC avoids this error, as DateTimeZone.UTC has no DST effects. With this, the code works independent of your system's default timezone.

The output is:

06/14/2017
06/14/2017


PS: As you care only about the date part (day/month/year), you could also use the org.joda.time.LocalDate class. To use it, just change the last part of the code (you can use the same parser):

// test with MM/dd/yyyy
LocalDate dt1 = parser.parseLocalDate("06/14/2017");
System.out.println(parser.print(dt1)); // 06/14/2017

// test with yyyy-MM-dd'T'HH:mm:ss
LocalDate dt2 = parser.parseLocalDate("2017-06-14T10:30:40");
System.out.println(parser.print(dt2)); // 06/14/2017

The output is the same:

06/14/2017
06/14/2017

Using a LocalDate is another way to avoid problems with Daylight Saving Time (as explained above). In this case, you don't need to set UTC, because LocalDate has no timezone information.

  • @AlexKornhauser I've updated the answer: making some tests I found some potencial errors with DST, so I fixed the code accordingly. –  Jun 14 '17 at 19:42
  • How would I do this same exact thing with the java.time library instead, eg DateTimeFormatter.ofPattern("MM/dd/yyyy"); + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); – AlexK Jun 17 '17 at 03:13
  • 1
    I don't think the new API has such feature, probably you'll need one formatter to parse the input (use a optional pattern `[MM/dd/yyyy][yyyy-MM-dd'T'HH:mm:ss]`) and another to format the output –  Jun 17 '17 at 11:22
2
private static final DateTimeFormatter SIMPLE_DATE_FORMAT = 
  DateTimeFormatter.ofPattern("[MM/dd/yy][M/dd/yy][MM/d/yy][M/d/yy]");

// value = "04/12/19"
// value = "4/12/19"
// value = "12/4/19"
// value = "2/4/19"

LocalDate.parse(value, SIMPLE_DATE_FORMAT);
tsunllly
  • 1,568
  • 1
  • 13
  • 15