-3

I have a use case where I have date in the following formats,

  1. 2023-05-09T05:55:07Z
  2. 2023-04-30
  3. 08-05-2023
  4. 2023-05-09 00:00:00
  5. 31-Dec-2021

Irrespective of the input format I have a requirement to convert it to dd-mm-yyyy format. How can I convert it in JDK11? I tried different libraries but got no luck.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Diksha Goyal
  • 260
  • 6
  • 19
  • 4
    Have you considered constructing a DateTimeFormatter for each format and then trying each one in turn? – jon hanson May 19 '23 at 13:09
  • I was actually looking for a solution in java the way we have in JS. just with d = new Date() & then d.getDate(), d.getMonth() .. so on – Diksha Goyal May 19 '23 at 13:37
  • 1
    Even in JS, the input format of what you parse matters. `new Date()` is just the current date, it's not parsing anything there. – Rogue May 19 '23 at 13:38
  • 1
    The example `08-05-2023` is quite dangerous because one cannot reliably derive if it is `MM-dd-uuuu` or `dd-MM-uuuu`. Those would both be parseable by a `DateTimeFormatter`, but the results will be significantly different. – deHaar May 19 '23 at 13:40
  • But within new Date() constructor you can pass any date format & it will give you a generic date object which have those methods – Diksha Goyal May 19 '23 at 13:40
  • 3
    Do not use the `java.util` package for dates. Use the `java.time` package that was introduced in Java 8. See my response below. – Mr. Polywhirl May 19 '23 at 13:40
  • 1
    @DikshaGoyal *But within new Date() constructor you can pass any date format & it will give you a generic date object which have those methods* -- This is incorrect. Look at your example above. Try passing in 08-05-2023, like you showed, and see what JavaScript gives you. It will not work. – davidalayachew May 19 '23 at 19:02
  • You tagged your question simpledateformat, but you should unconditionally avoid using `SimpleDateFormat` and `Date`. Those clauses were troublesome and are long outdated. Do use java.time and its `DateTimeFormatter` and other classes. – Ole V.V. May 19 '23 at 19:07
  • Your first format unlike the others includes an offset from UTC. In which time zone do you want the date in that case? And in the other cases, do you want to use the same time zone that was understood in the original string? – Ole V.V. May 19 '23 at 19:11
  • 1
    This Question was closed for the wrong reason. This is not an opinion-based question. It is however a duplicate of other Questions on how to parse date-time inputs with various formats. Voting to re-open. – Basil Bourque May 20 '23 at 18:44
  • 2
    Duplicate of [this](https://stackoverflow.com/q/51587242/642706), [this](https://stackoverflow.com/q/19137007/642706), [this](https://stackoverflow.com/q/4024544/642706), and more. – Basil Bourque May 20 '23 at 18:46

2 Answers2

2

You can iterate over known formats and skip ones that throw a DateTimeParseException.

Note: If you are parsing just a date string into a LocalDateTime, you will need to build the formatter and specify that the time portion is optional.

import java.time.LocalDateTime;
import java.time.format.*;
import java.time.temporal.ChronoField;
import java.util.*;

public class DateParser {
    public static void main(String[] args) {
        System.out.println(normalizeDate("2023-05-09T05:55:07Z")); // 2023-05-09
        System.out.println(normalizeDate("2023-04-30")); // 2023-04-30
        System.out.println(normalizeDate("08-05-2023")); // 2023-08-05
        System.out.println(normalizeDate("2023-05-09 00:00:00")); // 2023-05-09
        System.out.println(normalizeDate("31-Dec-2021")); // 2021-12-31
        System.out.println(normalizeDate("")); // null
    }

    public static String normalizeDate(String rawDateTimeString) {
        return Optional.ofNullable(parseDateString(rawDateTimeString))
                .map(displayFormat::format)
                .orElse(null);
    }

    private static LocalDateTime parseDateString(String dateString) {
        for (DateTimeFormatter formatter : knownFormats) {
            try {
                return LocalDateTime.parse(dateString, formatter);
            } catch (DateTimeParseException e) {
                continue;
            }
        }
        return null;
    }

    private static final List<DateTimeFormatter> knownFormats = Arrays.asList(
            DateTimeFormatter.ISO_DATE_TIME,
            dateOnlyFormatter("yyyy-MM-dd"),
            dateOnlyFormatter("MM-dd-yyyy"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            dateOnlyFormatter("dd-MMM-yyyy").withLocale(Locale.ENGLISH));

    private static DateTimeFormatter dateOnlyFormatter(String datePattern) {
        // Credit: https://stackoverflow.com/a/40175568/1762224
        return new DateTimeFormatterBuilder()
                .appendPattern(datePattern)
                .optionalStart()
                .appendPattern(" HH:mm")
                .optionalEnd()
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                .toFormatter();
    }

    private static final DateTimeFormatter displayFormat = DateTimeFormatter.ISO_DATE;
}

Here is a modified version that is simpler as Devin suggested. It ignores the concept of time entirely, and only focuses on date when parsing. It removes the need for the dateOnlyFormatter method (as seen above).

import java.time.LocalDate;
import java.time.format.*;
import java.util.*;

public class DateParser {
    public static void main(String[] args) {
        System.out.println(normalizeDate("2023-05-09T05:55:07Z")); // 2023-05-09
        System.out.println(normalizeDate("2023-04-30")); // 2023-04-30
        System.out.println(normalizeDate("08-05-2023")); // 2023-08-05
        System.out.println(normalizeDate("2023-05-09 00:00:00")); // 2023-05-09
        System.out.println(normalizeDate("31-Dec-2021")); // 2021-12-31
        System.out.println(normalizeDate("")); // null
    }

    public static String normalizeDate(String rawDateTimeString) {
        return Optional.ofNullable(parseDateString(rawDateTimeString))
                .map(displayFormat::format)
                .orElse(null);
    }

    private static LocalDate parseDateString(String dateString) {
        for (DateTimeFormatter formatter : knownFormats) {
            try {
                return LocalDate.parse(dateString, formatter);
            } catch (DateTimeParseException e) {
                continue;
            }
        }
        return null;
    }

    private static final List<DateTimeFormatter> knownFormats = Arrays.asList(
            DateTimeFormatter.ISO_DATE_TIME,
            DateTimeFormatter.ofPattern("yyyy-MM-dd"),
            DateTimeFormatter.ofPattern("MM-dd-yyyy"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            DateTimeFormatter.ofPattern("dd-MMM-yyyy").withLocale(Locale.ENGLISH));

    private static final DateTimeFormatter displayFormat = DateTimeFormatter.ISO_DATE;
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • 1
    @user16320675 I added `.withLocale(Locale.ENGLISH)` to the offending formatters above. – Mr. Polywhirl May 19 '23 at 14:47
  • Good Answer, but this has already been covered on multiple other existing Questions. – Basil Bourque May 20 '23 at 18:47
  • In real work, the locale should likely need to specify a culture and possibly a sub-culture. Date-time formats (abbreviation, punctuation, capitalization) can vary across various English-speaking places. – Basil Bourque May 20 '23 at 18:49
-1

This method is simple and easy to decipher. Here is all that the parseDateTime function does:

  1. Sets the output format
  2. Sets all possible input formats
  3. Iterates over each input format until the correct one is found
  4. Returns the date/time in the specified output format

Note If the String passed into the function is not in one of the 5 specified input formats, then it will return null. This should make it easy to see if it was successful or not!

If you need more information to help add more formats, here is the DateTimeFormatter Documentation: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_DATE

import java.time.*;
import java.time.format.DateTimeFormatter;

public class Main {
     public static void main(String[] args) {
        String[] inputDates = {
                "2023-04-30T05:55:07Z",
                "2023-04-30",
                "30-04-2023",
                "2023-04-30 00:00:00",
                "30-Apr-2023"
        };

        for(String date : inputDates)
            System.out.println(date + "\t->\t" + parseDateTime(date));
    }


    private static String parseDateTime(String currentDate){
        //Desired Output Pattern
        DateTimeFormatter outputFormat = DateTimeFormatter.ofPattern("dd-MM-yyyy");

        //All Possible Input Patterns
        DateTimeFormatter[] formatters = {
            DateTimeFormatter.ISO_LOCAL_DATE,
            DateTimeFormatter.ofPattern("dd-MM-yyyy"),
            DateTimeFormatter.ofPattern("dd-MMM-yyyy"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
        };

        //Iterate over each pattern until it finds the correct one
        for(DateTimeFormatter currFormatter : formatters){
            try{
                return LocalDate.parse(currentDate, currFormatter).format(outputFormat);
            } catch(Exception ignored){
                //Current formatter did not work, so lets try the next one
            }
        }
        return null; // String was not one of those 5 formats
    }
}```
TylerH
  • 20,799
  • 66
  • 75
  • 101
DevinJM3
  • 1
  • 1
  • 2
    Please be aware that parsing month names is `Locale` sensitive; and that `Z` can stand for a time zone, not necessarily the literal `Z` – user16320675 May 19 '23 at 14:26
  • *Never* put quote marks around the `Z` in a date-time formatting pattern. The quotes mean "expect but ignore this text". Ignoring that text is restless as the presence of a `Z` means "An offset from UTC of zero hours-minutes-seconds". You are discarding valuable information, with no benefit in return. I must down-vote on a solution that recommends data loss. – Basil Bourque May 20 '23 at 18:42