5

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
}
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Patrik
  • 63
  • 7
  • 2
    why don't you just use a dateformatter? – Stultuske Sep 05 '17 at 10:18
  • How can you check the pattern? Any day and month smaller or equal to 12 will be valid but could be the wrong way around... – mdon88 Sep 05 '17 at 10:18
  • 1
    Unless this is for *educational* purpose: do **not** re-invent the wheel. Java has a ton of options to do date parsing ... and when you restrain yourself to the things added with Java8 - you get a lot of very useful stuff. – GhostCat Sep 05 '17 at 10:24
  • I think you are on the right track: with each entry detect what date format you have, then try to read the date from this entry. – Developer Marius Žilėnas Sep 05 '17 at 10:31
  • 2
    This sounds impossible. How do you decide whether 11032017 is 11th of March or 3rd of November? In 11002017, how do you decide whether the day-of-month or the month is missing? – Ole V.V. Sep 05 '17 at 10:40
  • maybe i tell a litle bad, my englisch is not the best :/ my methode: private void parseString(String value, String pattern).. so i have the value like 01002017 and pattern like ddMMyyyy so i know the pattern and the value, but i have to convert the 00 to 01, when the pattern is MMyyyydd then i have maybe the value 11201700 now i have to change the day from 00 to 01 – Patrik Sep 05 '17 at 10:42
  • 2
    maybe better to understand: my methode get 2 strings: Value and pattern, when the value is 00082017 //ddMMyyyy and the pattern is ddMMyyyy now in my methode i have 3 Strings: day = month = and year = now i will fill the strings day = 00 month = 08 year = 2017 how can i do this? my example from the first post works, but what is when i get value 09002017 and pattern MMddyyyy then is it not working – Patrik Sep 05 '17 at 10:51
  • updated question – Patrik Sep 05 '17 at 11:00
  • You might [following example](https://gist.github.com/MenoData/14364a1bcd44468e705c3d6a0dab3d00) using my lib Time4J helpful (better in parsing and formatting than the standard). The crucial point is: 00 is interpreted as literal and not as value. Furthermore, an elegant or-syntax is possible by using the special pattern character "|". – Meno Hochschild Sep 05 '17 at 12:42
  • *I can have different patterns: ddMMyyyy or MMddyyyy or yyyy/dd/MM, etc* - is 01022017 with ddMMyyyy or MMddyyyy? How do you differ these two patterns? – Tom Sep 05 '17 at 14:08

1 Answers1

4

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
  • 1
    It seems to be illogical to allow zero day or month in strict mode. Really wondering. For a more elegant way, see this [example](https://gist.github.com/MenoData/14364a1bcd44468e705c3d6a0dab3d00). – Meno Hochschild Sep 05 '17 at 12:43
  • 1
    @MenoHochschild Indeed, I also thought that *strict* should mean *I don't accept strange values*, but it actually works. Bad name choice, maybe? –  Sep 05 '17 at 12:47
  • 1
    Within the scope of Java-8, I suggest you consider the alternative `parser.parseUnresolved("00092017", new ParsePosition(0))` because it does not seem to depend on the irrational behaviour of resolving style. – Meno Hochschild Sep 05 '17 at 13:01
  • 1
    @MenoHochschild Great, `parseUnresolved` works regardless of the resolving style. I've updated the answer, thanks a lot! –  Sep 05 '17 at 13:07
  • 1
    Thanks for the edit. I think that in most use cases, after `parseUnresolved()` I would want to check that the parse position indicates the entire string was parsed. This would enhance input validation. – Ole V.V. Sep 05 '17 at 13:51
  • @OleV.V. Indeed, I've added this to the answer, thanks a lot! –  Sep 05 '17 at 14:00
  • Sorry I was unclear, I was more thinking `pos.getIndex() < valueWithDate.length()`. @Hugo – Ole V.V. Sep 05 '17 at 14:05
  • 1
    @OleV.V. Well, I've updated the answer, thanks again! –  Sep 05 '17 at 14:11