-2

Is there any java library available to parse language specific ordinal indicator/suffix?

I have a date value like the following: 26th May 2017. I want to convert this to 26/05/2017. Could anyone please guide me how to do?

Arvinda
  • 89
  • 7
  • 4
    In Java or Javascript? What do Spring and Hibernate have to do this? What research have you done on the *many* questions about date format conversion? – Jon Skeet May 25 '17 at 16:47
  • I want to convert 26th May 2017 to 26/05/2017 in Java program. – Arvinda May 25 '17 at 16:55
  • Is there any better way to do this in java? – Arvinda May 26 '17 at 02:11
  • Fyi, ["ordinal"](https://en.wikipedia.org/wiki/Ordinal_number_(linguistics)) is the technical term to use is searching on this topic of `st`, `th`, `rd`, and `nd`. – Basil Bourque May 26 '17 at 05:06

4 Answers4

3

You can parse this format directly to a Java 8 LocalDate using a custom date format:

static final Map<Long, String> ORDINAL_DAYS = new HashMap<>();
static
 {
   ORDINAL_DAYS.put(1, "1st");
   .... more ....
   ORDINAL_DAYS.put(26, "26th");
   .... more ....
   ORDINAL_DAYS.put(31, "31st");
 }

static final DateTimeFormatter FORMAT_DAY_MONTH_YEAR = new DateTimeFormatterBuilder()
  .appendText(ChronoField.DAY_OF_MONTH, ORDINAL_DAYS)
  .appendLiteral(' ')
  .appendText(ChronoField.MONTH_OF_YEAR)
  .appendLiteral(' ')
  .appendText(ChronoField.YEAR)
  .toFormatter();


 String dateInString = "26th May 2017";

 LocalDate date = LocalDate.parse(dateInString, FORMAT_DAY_MONTH_YEAR);

This is using the version of DateTimeFormatter.appendText which accepts a map that is used to map the day string.

You will need to fill in all the missing entries in ORDINAL_DAYS that I have left out for brevity.

greg-449
  • 109,219
  • 232
  • 102
  • 145
  • More lenient, but also briefer: `static final DateTimeFormatter FORMAT_DAY_MONTH_YEAR = DateTimeFormatter.ofPattern("d['st']['nd']['rd']['th'] MMMM uuuu", Locale.ENGLISH);`. PS Consider specifying the locale. – Ole V.V. May 25 '17 at 20:11
2

Assuming you don’t need very strict input validation, since you are converting from the format with th on the number (or st or nd in 31st, 2nd and more), I suggest you simply remove those two letters first. A regex may do that:

    // remove st, nd, rd or th after day of month
    dateInString 
            = dateInString.replaceFirst("^(\\d+)(st|nd|rd|th)( \\w+ \\d+)$", "$1$3");
    String dateOutString = LocalDate.parse(dateInString, 
                    DateTimeFormatter.ofPattern("d MMM uuuu", Locale.ENGLISH))
            .format(DateTimeFormatter.ofPattern("dd/MM/uuuu"));

The result is

26/05/2017

This works if your input contains a three letter abbreviation for the month, like Apr, May or Jun. To accept a full month name instead (April, May, June), you need 4 Ms instead of 3 in the format pattern: d MMMM uuuu.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
2

As stated by @OleV.V. in this comment, you can use a pattern with optional sections (to parse the different suffixes st, nd, rd and th).

You must also use a java.util.Locale to force month names to English. The code will be like this:

String input = "26th May 2017";
DateTimeFormatter parser = DateTimeFormatter
    // parse the day followed by st, nd, rd or th (using optional patterns delimited by [])
    .ofPattern("dd['st']['nd']['rd']['th'] MMM yyyy")
    // force English locale to parse month names
    .withLocale(Locale.ENGLISH);
// formatter for dd/MM/yyyy output
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy").withLocale(Locale.ENGLISH);
System.out.println(formatter.format(parser.parse(input))); // 26/05/2017

The code above will work for month names with 3 letters (like May or Aug). If you want to parse the full names (like August or March), just change MMM to MMMM:

DateTimeFormatter parser = DateTimeFormatter
    // using MMMM to parse full month name (like "August")
    .ofPattern("dd['st']['nd']['rd']['th'] MMMM yyyy")
    .withLocale(Locale.ENGLISH);

PS: If you want to parse both cases (3-letter or full month names) using the same parser, you can do this:

DateTimeFormatter parser = DateTimeFormatter
    // can parse "March" or "Mar" (MMMM or MMM)
    .ofPattern("dd['st']['nd']['rd']['th'][ MMMM][ MMM] yyyy")
    .withLocale(Locale.ENGLISH);
  • 1
    It's a good answer, and I am happy if I have contributed to it or inspired it one way or the other. If so, a mention would be nice. In any case your explanations are probably better than mine, or at least different and thus a good supplement. – Ole V.V. May 26 '17 at 05:44
  • 1
    In both code snippets you may use the two-arg `DateTimeFormatter.ofPattern()` for a bit terser code. – Ole V.V. May 26 '17 at 08:48
  • 1
    @OleV.V. To be honest, I saw your comment with this solution just **after** I posted my answer (I just had a very similar code here, with optional patterns and so on, and as soon as I read the question, posted the answer without looking at anything else). Sorry if I gave the impression that I copied your idea without giving any credit. Anyway, I'm gonna edit the answer to add this info. And I always forget about the 2-arg version of `ofPattern` - I usually don't need to set different locales. Thanks! –  May 26 '17 at 11:50
  • 1
    I believe you. :-) – Ole V.V. May 26 '17 at 11:56
  • 1
    BTW I recommend making it a rule to use the two-arg `ofPattern()`. Even when you pass `Locale.getDefault()` as the second argument, you are telling the reader (and yourself!) that you have thought about the locale and made a conscious decision about which locale to use. – Ole V.V. May 26 '17 at 15:49
1

Assuming you are asking about Java, this link: https://www.mkyong.com/java/how-to-convert-string-to-date-java/ May help you.

The overall gist is:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestDateExample3 {

    public static void main(String[] argv) {



        SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");

        String dateInString = "26th May 2017"; // Remove your 'th', 'nd', etc. from the input string.

        String withoutEnding = dateInString;
        //Something like this
        if(dateInString.contains("th") withoutEnding = dateInString.replace("th", "");
        if(dateInString.contains("nd") withoutEnding = dateInString.replace("nd", "");
        if(dateInString.contains("st") withoutEnding = dateInString.replace("st", "");
        if(dateInString.contains("rd") withoutEnding = dateInString.replace("rd", "");

        try {

            Date date = formatter.parse(withoutEnding);
            System.out.println(date);
            System.out.println(formatter.format(date));

        } catch (ParseException e) {
            e.printStackTrace();
        }

    }

}

Where dd/MM/yyyy is a date formatter that would give you 26/05/2017.

Hope this helps!

EDIT: Also see http://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html for a full list of the different pattern letters for SimpleDateFormat.

Chris Gilardi
  • 1,509
  • 14
  • 26