Java date format for 00 days/months

1.3k views Asked by At

I have a date as String, but sometimes it doesn't have valid day or month (the values are zeros, 00).

Example:

String value = "03082017";

Then I do:

    String day = value.substring(0,2);
    String month = value.substring(2,4);
    String year = value.substring(4,8);

    if(day.equals("00")) {
        day = "01";
    }
    if(month.equals("00")) {
        month = "01";
    }

    value = day + month + year;

This works for the example String.

Now I have this String:

String value = "00092017" //ddMMyyyy 

Then my code converts the 00 day to 01.

This works for the pattern ddMMyyyy, but now is my problem: I can have different patterns: ddMMyyyy or MMddyyyy or yyyy/dd/MM, etc

My solution is to check first the pattern (such as MMddyyyy) and then I look my value 03092017 (ddMMyyyy) and take the numbers to my day string where is in the position dd from my pattern.

So the code has the pattern ddMMyyyy but value is 03092017 (MMddyyyy)

String day = "03";
.....

My code:

public void doSomething(String valueWithDate, String pattern){
    // now I become: 
    // valueWithDate = 01092017
    // pattern = ddMMyyyy

    String day = valueWithDate.substring(0,2);
    String month = valueWithDate.substring(2,4);
    String year = valueWithDate.substring(4,8);

    //Works I get my information but what is when I get other pattern/value? for example:
    // valueWithDate = 09082017
    // pattern = MMddyyyy
    // now I want my information again, but day is not on substring 0,2
}
1

There are 1 answers

8
AudioBubble On BEST ANSWER

If you're using Java 8, you can use the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

You could do that using a DateTimeFormatter with strict mode. This allows values like zero in the day and month. The other modes don't work: default mode doesn't accept zeroes, and lenient mode tries to do automatic conversions, so the strict mode is the only one that works in this case.

Then you get the individual fields (day and month) and check if they are zero. I'm using the getLong method (and casting to an int), because the get method (that returns an int) checks if the values returned are valid (so values like zero throw an exception).

Finally, you join all the pieces to get the output:

String valueWithDate = "00092017";
String pattern = "ddMMyyyy";
// create formatter with the pattern and strict mode
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern).withResolverStyle(ResolverStyle.STRICT);
// parse the input
TemporalAccessor parsed = fmt.parse(valueWithDate);

// get day from the parsed object
int day = (int) parsed.getLong(ChronoField.DAY_OF_MONTH);
if (day == 0) {
    day = 1;
}
// get month from the parsed object
int month = (int) parsed.getLong(ChronoField.MONTH_OF_YEAR);
if (month == 0) {
    month = 1;
}
// get year from the parsed object
int year = (int) parsed.getLong(ChronoField.YEAR_OF_ERA);

// the final value will have the same pattern? if so, just use the same formatter
String value = fmt.format(LocalDate.of(year, month, day));
System.out.println(value); // 01092017

So, for the input 00092017 and pattern ddMMyyyy, the code above will print:

01092017


As suggested by @MenoHochschild's comment, you can also use the parseUnresolved method, which doesn't validate the values, so it allows zero in day and month (and you don't need to set the formatter to strict mode).

This method also needs a java.text.ParsePosition, that is used to check for parsing errors (because it doesn't throw an exception like the parse method does):

// create formatter, no need of strict mode
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern);
// parse
ParsePosition pos = new ParsePosition(0);
TemporalAccessor parsed = fmt.parseUnresolved(valueWithDate, pos);
// check errors
if (pos.getErrorIndex() >= 0) {
    // error in parsing (getErrorIndex() returns the index where the error occurred)
}
// rest of the code is the same

As @OleV.V.'s comment suggested, you can also check if the whole input wasn't parsed, using:

if(pos.getIndex() < valueWithDate.length()) {
    // the input String wasn't fully parsed
}

I'm assuming that the final value will have the same format used in the input (the same pattern passed to the method).

If you want the value in another format, though, just create another formatter with a different pattern:

// output in another format
DateTimeFormatter output = DateTimeFormatter.ofPattern("MMddyyyy");
String value = output.format(LocalDate.of(year, month, day));
System.out.println(value); // 09012017