Search code examples
javaandroidkotlindate

How to convert the input format of date to a specific new format in android


How to convert the value in the below input to the format of 9:15 pm, 2nd May 2023

Input: 2023-11-20T17:09:08.000Z


What I tried: I tried to use SimpleDateFormat, But I couldn't get the appropriate format

val curFormater = SimpleDateFormat("dd-MM-yyyy")

Any other attempts result in a parse error ... Is there a proper standard way to achieve this?


Solution

  • There is. As rzwitserloot already said, immediately ditch the obsolete SimplateDateFormat. Use classes from the java.time package instead.

    Then you need to do the following:

    1. Parse the string

    to a proper date and time model. I picked OffsetDateTime here, as your input string contains a date component, a time component and a time zone thingy (the Z).

    OffsetDateTime dateTime = OffsetDateTime.parse(input);
    

    Since the input format is conform to the ISO 8601 standard, you don't have to specify a format; it will parse the string out-of-the-box.

    2. Define the right output format

    and you can use format to form the desired text. This looks like it:

    String output = dateTime.format(DateTimeFormatter.ofPattern("h:mm a, d MMMM uuuu", Locale.UK));
    

    Ordinal suffix

    But then there is a small problem. You are using an English ordinal suffix (st, nd, rd, th), and there is no such formatting pattern to output such. Fortunately, you can use a DateTimeFormatterBuilder to have a more fine-grained control over the output. We can use appendText(Map<Long, String>) to look up a value mapped to a certain comnponent field.

    final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .appendPattern("h:mm a, d")
        .appendText(ChronoField.DAY_OF_MONTH, ordinalSuffixes)
        .appendPattern(" MMMM uuuu")
        .toFormatter(Locale.UK);
    

    This will use the day of the month as a long to lookup that specific value in the given map. So we will need to make sure that we have a map which returns the correct values for the correct keys.

    Map<Long, String> ordinalSuffixes = IntStream.rangeClosed(1, 31)
        .mapToObj(n -> Map.entry((long) n, switch (n) {
            case 1, 21, 31 -> "st";
            case 2, 22 -> "nd";
            case 3, 23 -> "rd";
            default -> "th";
        }))
        .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));