184

I know this will give me the day of the month as a number (11, 21, 23):

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

But how do you format the day of the month to include an ordinal indicator, say 11th, 21st or 23rd?

Paolo Forgia
  • 6,572
  • 8
  • 46
  • 58
Ken Hume
  • 1,935
  • 2
  • 13
  • 7
  • 14
    For reference these are called ordinal numbers - http://en.wikipedia.org/wiki/Ordinal_number_(linguistics). – ocodo Oct 25 '10 at 01:23
  • 5
    Just for the record, anything constructing the response instead of looking up the _whole_ answer in a table is close to impossible to localize to other languages. – Thorbjørn Ravn Andersen Oct 25 '10 at 02:58
  • 2
    The answer is somehow incorrect have a look at my answer plz. – J888 Jul 31 '13 at 03:15
  • 4
    Modern comment: I recommend you avoid the `SimpleDateFormat` class. It is not only long outdated, it is also notoriously troublesome. Today we have so much better in [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter`. – Ole V.V. May 16 '18 at 11:43
  • Take a look at the numbers API (https://math.tools/api/numbers/). It has support for ordinal, cardinal , number spelled out in different language, spelled out as currency in various languages etc. – dors Jul 01 '19 at 14:13
  • @OleV.V. java.time is not supported in android – Gilbert Jan 20 '21 at 21:28
  • Oh yes, @Ssenyonjo, java.time is supported on Android alright. From Android API level 26 it is built in. For lower API levels it’s available through [desugaring](https://developer.android.com/studio/write/java8-support-table). – Ole V.V. Jan 21 '21 at 03:11
  • I think this post will help you. [https://stackoverflow.com/a/66816360/12883809](https://stackoverflow.com/a/66816360/12883809) – Aziz Ejaz Mar 26 '21 at 11:51

23 Answers23

215
// https://github.com/google/guava
import static com.google.common.base.Preconditions.*;

String getDayOfMonthSuffix(final int n) {
    checkArgument(n >= 1 && n <= 31, "illegal day of month: " + n);
    if (n >= 11 && n <= 13) {
        return "th";
    }
    switch (n % 10) {
        case 1:  return "st";
        case 2:  return "nd";
        case 3:  return "rd";
        default: return "th";
    }
}

The table from @kaliatech is nice, but since the same information is repeated, it opens the chance for a bug. Such a bug actually exists in the table for 7tn, 17tn, and 27tn (this bug might get fixed as time goes on because of the fluid nature of StackOverflow, so check the version history on the answer to see the error).

Cœur
  • 37,241
  • 25
  • 195
  • 267
Greg Mattes
  • 33,090
  • 15
  • 73
  • 105
  • 1
    Yeah, but @kaliatech can fix his table whereas your algorithm cannot be adjusted to correctly handle the 11th, 12th and 13th of the month. – Larry Lustig Oct 25 '10 at 00:52
  • 42
    it really feels like an oversight in the simple data format, doesn't it? – stevevls Dec 11 '11 at 14:11
  • 2
    @J888 The ordinal-suffix style is hardcoded, but that's exactly the context of this question. – Greg Mattes Aug 01 '13 at 16:05
  • 65
    Have fun internationalising this. :D – Hakanai May 20 '14 at 05:49
  • 8
    @Trejkaz Outside of the scope of the question :) – Greg Mattes May 23 '14 at 23:45
  • 2
    @GregMattes who said it was in scope? It's something which always comes up eventually. (If the software is decent.) – Hakanai May 24 '14 at 00:12
  • 1
    This answer needs to be downvoted to oblivion- it will horribly fail in any language other than english – Gabe Sechan Mar 05 '15 at 00:30
  • What if we want to do this for a different locale ? – user1841702 Dec 19 '16 at 09:45
  • 1
    Chuck Norris can check bounds of an argument without `Preconditions.` – Dávid Horváth Jan 26 '17 at 17:08
  • 7
    This solution supports only English. Use RuleBasedNumberFormat from ICU4J for the localized solution. – Дмитро Яковлєв Apr 04 '18 at 10:06
  • 2
    @Trejkaz and the _this cannot be internationalized_ crowd in general, how does one "internationalize this"? There is going to be a different _algorithm_ for almost every language anyway. Why having to rewrite the algo for the new language is significantly worse than having to create the table for the new language? Can you clarify (better if with an example) the disadvantages that you are considering? – SantiBailors Apr 04 '18 at 14:16
  • @SantiBailors In this particular case, I think CLDR has already built the tables for a large number of languages, so the effort required to build the table itself is zero. The table more or less contains algorithms though, if you look at it, so it essentially comes down to implementing parsing of that expression language. I'm fairly sure ICU will already have that job done, so my own preference would be to use ICU. – Hakanai Apr 05 '18 at 00:00
  • @Trejkaz I too would go for existing standards / libraries, but your comment meant that this solution was bad in absolute, not that it was just worse than some other one (none was mentioned anyway). No already existing alternative was mentioned, that's why I wondered why it's better to have to create a new table instead of a new algorithm (which I think is not). – SantiBailors Apr 05 '18 at 05:43
  • 13
    You guys are off in the weeds, and the tone is taking a bit of turn. No need for that. This question didn't ask about the intricacies of internationalization. Internationalization is certainly a fine topic to discuss, but it should be done on another question with that focus. I've always interpreted this question as asking for a quick and dirty way to accomplish this in English. So, I suggest that if there's really no good solution in the Java world that is internationalization friendly that we start a project on, e.g., GitHub and create a good solution. Let me know if you're interested. – Greg Mattes Apr 07 '18 at 13:56
  • 2
    @GregMattes I am interested – Gilbert Jan 20 '21 at 21:33
  • 1
    @Ssenyonjo post a link to the repos. – Greg Mattes Jan 28 '21 at 17:22
60

There is nothing in JDK to do this.

  static String[] suffixes =
  //    0     1     2     3     4     5     6     7     8     9
     { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th",
  //    10    11    12    13    14    15    16    17    18    19
       "th", "th", "th", "th", "th", "th", "th", "th", "th", "th",
  //    20    21    22    23    24    25    26    27    28    29
       "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th",
  //    30    31
       "th", "st" };

 Date date = new Date();
 SimpleDateFormat formatDayOfMonth  = new SimpleDateFormat("d");
 int day = Integer.parseInt(formatDateOfMonth.format(date));
 String dayStr = day + suffixes[day];

Or using Calendar:

 Calendar c = Calendar.getInstance();
 c.setTime(date);
 int day = c.get(Calendar.DAY_OF_MONTH);
 String dayStr = day + suffixes[day];

Per comments by @thorbjørn-ravn-andersen, a table like this can be helpful when localizing:

  static String[] suffixes =
     {  "0th",  "1st",  "2nd",  "3rd",  "4th",  "5th",  "6th",  "7th",  "8th",  "9th",
       "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
       "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "29th",
       "30th", "31st" };
kaliatech
  • 17,579
  • 5
  • 72
  • 84
  • 8
    If you let the table contain the full "21st", "23rd", "29th" it can be externalized and localized to other languages. For successful software that may become a requirement. – Thorbjørn Ravn Andersen Oct 25 '10 at 02:59
45
private String getCurrentDateInSpecificFormat(Calendar currentCalDate) {
    String dayNumberSuffix = getDayNumberSuffix(currentCalDate.get(Calendar.DAY_OF_MONTH));
    DateFormat dateFormat = new SimpleDateFormat(" d'" + dayNumberSuffix + "' MMMM yyyy");
    return dateFormat.format(currentCalDate.getTime());
}

private String getDayNumberSuffix(int day) {
    if (day >= 11 && day <= 13) {
        return "th";
    }
    switch (day % 10) {
    case 1:
        return "st";
    case 2:
        return "nd";
    case 3:
        return "rd";
    default:
        return "th";
    }
}
Stephen
  • 1,737
  • 2
  • 26
  • 37
Anand Shekhar
  • 467
  • 4
  • 2
  • 2
    I just tried your solution while omitting the single quotes in the pattern string provided to the SimpleDateFormat constructor. I now see why they're there. I'm getting an `java.lang.IllegalArgumentException` because of "an illegal pattern character, t." – danmaze May 30 '20 at 09:47
33

I should like to contribute the modern answer. The SimpleDateFormat class was OK to use when the question was asked 8 years ago, but you should avoid it now as it is not only long outdated, but also notoriously troublesome. Use java.time instead.

Edit

DateTimeFormatterBuilder.appendText(TemporalField, Map<Long, String>) is great for this purpose. Using it we build a formatter that does the work for us:

    Map<Long, String> ordinalNumbers = new HashMap<>(42);
    ordinalNumbers.put(1L, "1st");
    ordinalNumbers.put(2L, "2nd");
    ordinalNumbers.put(3L, "3rd");
    ordinalNumbers.put(21L, "21st");
    ordinalNumbers.put(22L, "22nd");
    ordinalNumbers.put(23L, "23rd");
    ordinalNumbers.put(31L, "31st");
    for (long d = 1; d <= 31; d++) {
        ordinalNumbers.putIfAbsent(d, "" + d + "th");
    }

    DateTimeFormatter dayOfMonthFormatter = new DateTimeFormatterBuilder()
            .appendText(ChronoField.DAY_OF_MONTH, ordinalNumbers)
            .appendPattern(" MMMM")
            .toFormatter();

    LocalDate date = LocalDate.of(2018, Month.AUGUST, 30);
    for (int i = 0; i < 6; i++) {
        System.out.println(date.format(dayOfMonthFormatter));
        date = date.plusDays(1);
    }

The output from this snippet is:

30th August
31st August
1st September
2nd September
3rd September
4th September

Old answer

This code is shorter, but IMHO not so elegant.

    // ordinal indicators by numbers (1-based, cell 0 is wasted)
    String[] ordinalIndicators = new String[31 + 1];
    Arrays.fill(ordinalIndicators, 1, ordinalIndicators.length, "th");
    ordinalIndicators[1] = ordinalIndicators[21] = ordinalIndicators[31] = "st";
    ordinalIndicators[2] = ordinalIndicators[22] = "nd";
    ordinalIndicators[3] = ordinalIndicators[23] = "rd";

    DateTimeFormatter dayOfMonthFormatter = DateTimeFormatter.ofPattern("d");

    LocalDate today = LocalDate.now(ZoneId.of("America/Menominee")).plusWeeks(1);
    System.out.println(today.format(dayOfMonthFormatter) 
                        + ordinalIndicators[today.getDayOfMonth()]);

Running this snippet just now I got

23rd

One of the many features of java.time is that it’s straightforward and reliable to get the day of month as an int, which is obviously needed for picking the right suffix from the table.

I recommend you write a unit test too.

PS A similar formatter can also be used for parsing a date string containing ordinal numbers like 1st, 2nd, etc. That was done in this question: Java - Parse date with optional seconds.

Link: Oracle tutorial: Date Time explaining how to use java.time.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Would love to get this answer bumped up higher as this more modern answer is better than trying to roll your own implementation. – Krease Nov 20 '18 at 18:47
19

Question is little old. As this question is very noisy so posting what I did solved with static method as a util. Just copy, paste and use it!

 public static String getFormattedDate(Date date){
            Calendar cal=Calendar.getInstance();
            cal.setTime(date);
            //2nd of march 2015
            int day=cal.get(Calendar.DATE);

            if(!((day>10) && (day<19)))
            switch (day % 10) {
            case 1:  
                return new SimpleDateFormat("d'st' 'of' MMMM yyyy").format(date);
            case 2:  
                return new SimpleDateFormat("d'nd' 'of' MMMM yyyy").format(date);
            case 3:  
                return new SimpleDateFormat("d'rd' 'of' MMMM yyyy").format(date);
            default: 
                return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
        }
        return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
    }

For testing purose

Example: calling it from main method!

Date date = new Date();
        Calendar cal=Calendar.getInstance();
        cal.setTime(date);
        for(int i=0;i<32;i++){
          System.out.println(getFormattedDate(cal.getTime()));
          cal.set(Calendar.DATE,(cal.getTime().getDate()+1));
        }

Output:

22nd of February 2018
23rd of February 2018
24th of February 2018
25th of February 2018
26th of February 2018
27th of February 2018
28th of February 2018
1st of March 2018
2nd of March 2018
3rd of March 2018
4th of March 2018
5th of March 2018
6th of March 2018
7th of March 2018
8th of March 2018
9th of March 2018
10th of March 2018
11th of March 2018
12th of March 2018
13th of March 2018
14th of March 2018
15th of March 2018
16th of March 2018
17th of March 2018
18th of March 2018
19th of March 2018
20th of March 2018
21st of March 2018
22nd of March 2018
23rd of March 2018
24th of March 2018
25th of March 2018
Sarz
  • 1,970
  • 4
  • 23
  • 43
19
String ordinal(int num)
{
    String[] suffix = {"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"};
    int m = num % 100;
    return String.valueOf(num) + suffix[(m > 3 && m < 21) ? 0 : (m % 10)];
}
ocodo
  • 29,401
  • 18
  • 105
  • 117
9

If you try to be aware of i18n the solution get even more complicated.

The problem is that in other languages the suffix may depend not only on the number itself, but also on the noun it counts. For example in Russian it would be "2-ой день", but "2-ая неделя" (these mean "2nd day", but "2nd week"). This is not apply if we formatting only days, but in a bit more generic case you should be aware of complexity.

I think nice solution (I didn't have time to actually implement) would be to extend SimpleDateFormetter to apply Locale-aware MessageFormat before passing to the parent class. This way you would be able to support let say for March formats %M to get "3-rd", %MM to get "03-rd" and %MMM to get "third". From outside this class looks like regular SimpleDateFormatter, but supports more formats. Also if this pattern would be by mistake applied by regular SimpleDateFormetter the result would be incorrectly formatted, but still readable.

C.A.B.
  • 465
  • 4
  • 12
  • Good point about genders in Russian, but that would technically make %MMM impossible without additional context. – Mad Physicist Jan 25 '18 at 01:22
  • @Mad Physicist, this is not true as %MMM will be applied to month, so we know the noun to conjugate. – C.A.B. Dec 15 '18 at 21:43
8

Many of the examples here will not work for 11, 12, 13. This is more generic and will work for all case.

switch (date) {
                case 1:
                case 21:
                case 31:
                    return "" + date + "st";

                case 2:
                case 22:
                    return "" + date + "nd";

                case 3:
                case 23:
                    return "" + date + "rd";

                default:
                    return "" + date + "th";
}
sivag1
  • 4,734
  • 3
  • 32
  • 35
6

RuleBasedNumberFormat in ICU library

I appreciated the link to the ICU project's library from @Pierre-Olivier Dybman (http://www.icu-project.org/apiref/icu4j/com/ibm/icu/text/RuleBasedNumberFormat.html), however still had to figure out how to use it, so an example of the RuleBasedNumberFormat usage is below.

It will only format single number rather than the whole date, so you will need to build a combined string if looking for a date in format: Monday 3rd February, for example.

The below code sets up the RuleBasedNumberFormat as an Ordinal format for a given Locale, creates a java.time ZonedDateTime, and then formats the number with its ordinal into a string.

RuleBasedNumberFormat numOrdinalFormat = new RuleBasedNumberFormat(Locale.UK,
    RuleBasedNumberFormat.ORDINAL);
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Pacific/Auckland"));

String dayNumAndOrdinal = numOrdinalFormat.format(zdt.toLocalDate().getDayOfMonth());

Example output:

3rd

Or

4th

etc.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
tbigby
  • 163
  • 1
  • 9
6

Using the new java.time package and the newer Java switch statement, the following easily allows an ordinal to be placed on a day of the month. One drawback is that this does not lend itself to canned formats specified in the DateFormatter class.

Simply create a day of some format but include %s%s to add the day and ordinal later.

ZonedDateTime ldt = ZonedDateTime.now();
String format = ldt.format(DateTimeFormatter
        .ofPattern("EEEE, MMMM '%s%s,' yyyy hh:mm:ss a zzz"));

Now pass the day of the week and the just formatted date to a helper method to add the ordinal day.


int day = ldt.getDayOfMonth();
System.out.println(applyOrdinalDaySuffix(format, day));

Prints

Tuesday, October 6th, 2020 11:38:23 AM EDT

Here is the helper method.

Using the Java 14 switch expressions makes getting the ordinal very easy.

public static String applyOrdinalDaySuffix(String format,
        int day) {
    if (day < 1 || day > 31)
        throw new IllegalArgumentException(
                String.format("Bad day of month (%s)", day));
    String ord = switch (day) {
        case 1, 21, 31 -> "st";
        case 2, 22 -> "nd";
        case 3, 23 -> "rd";
        default -> "th";
    };
    
    return String.format(format, day, ord);
}
WJS
  • 36,363
  • 4
  • 24
  • 39
5

I can't be satisfied by the answers calling for a English-only solution based on manual formats. I've been looking for a proper solution for a while now and I finally found it.

You should be using RuleBasedNumberFormat. It works perfectly and it's respectful of the Locale.

3

Only issue with the solution provided by Greg is that it does not account for number greater than 100 with the "teen" numbers ending. For example, 111 should be 111th, not 111st. This is my solution:

/**
 * Return ordinal suffix (e.g. 'st', 'nd', 'rd', or 'th') for a given number
 * 
 * @param value
 *           a number
 * @return Ordinal suffix for the given number
 */
public static String getOrdinalSuffix( int value )
{
    int hunRem = value % 100;
    int tenRem = value % 10;

    if ( hunRem - tenRem == 10 )
    {
        return "th";
    }
    switch ( tenRem )
    {
    case 1:
        return "st";
    case 2:
        return "nd";
    case 3:
        return "rd";
    default:
        return "th";
    }
}
Talisphere
  • 63
  • 2
  • 7
    In what case would a Day Month sequence have more than 31 days?? – SatanEnglish Jan 30 '14 at 21:58
  • @SatanEnglish, the good thing about this static factory method is that it can be used for more than just getting the suffix of a month. :) – Jared Rummler May 15 '15 at 23:31
  • 1
    This method returns st for 11, nd for 12 and rd for 13 – TheIT Jun 08 '15 at 18:37
  • @SatanEnglish. Given that today is Febrember 137th in my calendar of choice, I think your question answers itself. Seriously though, non-Gregorian calendars abound if you know where to look. – Mad Physicist Jan 25 '18 at 01:25
  • No,@TheIT, it does not. I tested. It returns `th` for 11, 12 and 13 as it should. I believe the `if ( hunRem - tenRem == 10 )` case makes sure it does. – Ole V.V. Nov 09 '18 at 03:14
3

There is a simpler and sure way of doing this. The function you'll need to use is getDateFromDateString(dateString); It basically removes the st/nd/rd/th off of a date string and simply parses it. You can change your SimpleDateFormat to anything and this will work.

public static final SimpleDateFormat sdf = new SimpleDateFormat("d");
public static final Pattern p = Pattern.compile("([0-9]+)(st|nd|rd|th)");

private static Date getDateFromDateString(String dateString) throws ParseException {
     return sdf.parse(deleteOrdinal(dateString));
}

private static String deleteOrdinal(String dateString) {
    Matcher m = p.matcher(dateString);
    while (m.find()) {
        dateString = dateString.replaceAll(Matcher.quoteReplacement(m.group(0)), m.group(1));
    }
    return dateString;

}

  • 1
    This Answer is about *parsing* a String while the Question is about *generating* a String. But still appropriate as one is likely to need both directions. Also, this Answer solves [this other Question](http://stackoverflow.com/q/33389982/642706). – Basil Bourque Oct 28 '15 at 18:38
2

I wrote my self a helper method to get patterns for this.

public static String getPattern(int month) {
    String first = "MMMM dd";
    String last = ", yyyy";
    String pos = (month == 1 || month == 21 || month == 31) ? "'st'" : (month == 2 || month == 22) ? "'nd'" : (month == 3 || month == 23) ? "'rd'" : "'th'";
    return first + pos + last;
}

and then we can call it as

LocalDate localDate = LocalDate.now();//For reference
int month = localDate.getDayOfMonth();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(getPattern(month));
String date = localDate.format(formatter);
System.out.println(date);

the output is

December 12th, 2018
SamHoque
  • 2,978
  • 2
  • 13
  • 43
  • This requires a minimum API of 26 though :) – Mikkel Larsen Feb 07 '19 at 11:18
  • @MikkelLarsen this question wasn't about android, I was using Java 8 for this. Android doesn't support a large api for java 8. – SamHoque Feb 09 '19 at 06:06
  • @SamSakerz my bad :) – Mikkel Larsen Feb 11 '19 at 09:11
  • @MikkelLarsen Most *java.time* functionality is back-ported to Java 6 & Java 7 in the [***ThreeTen-Backport***](http://www.threeten.org/threetenbp/) project. Further adapted for earlier Android (<26) in [***ThreeTenABP***](https://github.com/JakeWharton/ThreeTenABP). See [*How to use ThreeTenABP…*](http://stackoverflow.com/q/38922754/642706). – Basil Bourque Jan 20 '20 at 04:43
2

Try below function:

public static String getFormattedDate(Date date) 
{
  Calendar cal = Calendar.getInstance();
  cal.setTime(date);
  //2nd of march 2015
  int day = cal.get(Calendar.DATE);
  if (!((day > 10) && (day < 19)))
   switch (day % 10) {
    case 1:
     return new SimpleDateFormat("d'st' 'of' MMMM yyyy").format(date);
    case 2:
     return new SimpleDateFormat("d'nd' 'of' MMMM yyyy").format(date);
    case 3:
     return new SimpleDateFormat("d'rd' 'of' MMMM yyyy").format(date);
    default:
     return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
   }
  return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
}
Prashant Pimpale
  • 10,349
  • 9
  • 44
  • 84
MEGHA DOBARIYA
  • 1,622
  • 9
  • 7
  • When answering an old question, your answer would be much more useful to other StackOverflow users if you included some context to explain how your answer helps, particularly for a question that already has an accepted answer. See: [How do I write a good answer](https://stackoverflow.com/help/how-to-answer). – David Buck Jan 27 '20 at 06:42
  • Thanks for wanting to contribute. I recommend we don’t use `Date`, `Calendar` and `SimpleDateFormat`. Those classes are poorly designed and long outdated, the last in particular notoriously troublesome. Instead use `LocalDate` and `DateTimeFormatter`, both from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). Also I’m in doubt what new your answer contributes compared to the other 18 answers? – Ole V.V. Jan 27 '20 at 19:10
  • For 12thi, it will print "12nd" which is incorrect. Similarly for 13th – Abhishek Saxena Oct 13 '20 at 11:57
2

For Kotlin try this

fun Int.ordinalAbbrev() =
        if (this % 100 / 10 == 1) "th"
        else when (this % 10) { 1 -> "st" 2 -> "nd" 3 -> "rd" else -> "th" }

it takes int value and returns like this '3rd' '1st' '11th' '2nd'. So you can use it for date format also.

Usage

fun getFormatedDate(date: String): String {
        date.let {
            try {
                val parser = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
                val formatter = SimpleDateFormat("dd MMMM", Locale.getDefault())
                val dateArray = formatter.format(parser.parse(it)).split(" ").toTypedArray()
                val formatedDate = String.format(
                    "${dateArray[0]}${
                        dateArray[0].toInt().ordinalAbbrev()
                    } ${dateArray[1]}"
                )

                return formatedDate
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return date
    }
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Aziz Ejaz
  • 57
  • 2
  • 1
    Not sure if you noticed - the OP was in Java? And has been aanswered 21 times. What does you answer offer that the others didn't? – Mr R Mar 26 '21 at 12:59
  • I don't know about others. but my answer is for kotlin developers and it is more helpfull. – Aziz Ejaz Mar 26 '21 at 13:48
  • Could you update the answer @AzizEjaz to mention Kotlin. – Mr R Mar 26 '21 at 14:29
2

Here is my answer:

public String getOrdinal(int day) { 
    String ordinal; 
    switch (day % 20) { 
        case 1: 
            ordinal = "st"; 
            break; 
        case 2: 
            ordinal = "nd"; 
            break; 
        case 3: 
            ordinal = "rd"; 
            break; 
        default: 
            ordinal = day > 30 > "st" : "th"; 
    } 
    return ordinal; 
} 

Just do the modulo with 20 and it will work for all dates. To get today's day you can use LocalDate.now().getDayOfMonth(). or pass any day like this

LocalDate.getDayOfMonth()
Zohra Khan
  • 5,182
  • 4
  • 25
  • 34
  • @OleV.V. Thanks for pointing out this bug! I am using this code in the live application I will correct it over there. I have posted correct code. Please check – Zohra Khan Oct 28 '21 at 08:17
  • 1
    Your code looks to me to be correct for 1 through 31 now. Only not the easiest to understand. Allow me to suggest you write a unit test. Also I would have wanted to add a range check and throw an exception if the `day` is less than 1 or greater than 31 since the method does not always produce the expected results for such numbers. – Ole V.V. Oct 28 '21 at 08:56
  • @OleV.V. Here I am assuming that day we are getting through LocalDate , so the day will be in the range of 1 to 31 – Zohra Khan Oct 28 '21 at 09:01
1

Here is an approach that updates a DateTimeFormatter pattern with the correct suffix literal if it finds the pattern d'00', e.g. for day of month 1 it would be replaced with d'st'. Once the pattern has been updated it can then just be fed into the DateTimeFormatter to do the rest.

private static String[] suffixes = {"th", "st", "nd", "rd"};

private static String updatePatternWithDayOfMonthSuffix(TemporalAccessor temporal, String pattern) {
    String newPattern = pattern;
    // Check for pattern `d'00'`.
    if (pattern.matches(".*[d]'00'.*")) {
        int dayOfMonth = temporal.get(ChronoField.DAY_OF_MONTH);
        int relevantDigits = dayOfMonth < 30 ? dayOfMonth % 20 : dayOfMonth % 30;
        String suffix = suffixes[relevantDigits <= 3 ? relevantDigits : 0];
        newPattern = pattern.replaceAll("[d]'00'", "d'" + suffix + "'");
    }

    return newPattern;
}

It does require that the original pattern is updated just prior to every formatting call, e.g.

public static String format(TemporalAccessor temporal, String pattern) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(updatePatternWithDayOfMonthSuffix(temporal, pattern));
    return formatter.format(temporal);
}

So this is useful if the formatting pattern is defined outside of Java code, e.g. a template, where as if you can define the pattern in Java then the answer by @OleV.V. might be more appropriate

tazmaniax
  • 406
  • 1
  • 6
  • 13
  • 1
    Creative and convoluted. It certainly took me a long time to understand how it works. That’s not a sign of good code. – Ole V.V. Nov 09 '18 at 03:06
  • 1
    @OleV.V. thx for the feedback - I've restructured it a bit so it's a tiny bit less verbose. Just seen your approach and I like it! I think both approaches are valid with different trade offs. Yours doesn't require any further support at the point of formatting but does require the pattern is defined using the builder that precludes pattern definition in non Java code. My approach does require additional support at point of formatting but no dependency on the builder to create the pattern so makes it a bit more flexible where the pattern can be defined. My requirements dictated the latter – tazmaniax Nov 12 '18 at 06:32
  • 3
    Very nice account of pros and cons. You may want to include it in the answer? Just an idea. – Ole V.V. Nov 12 '18 at 07:00
1
public static String getReadableDate(final int date){
    String suffix = "th";
    switch (date){
        case 1:
        case 21:
        case 31:
            suffix = "st";
            break;
        case 2:
        case 22:
            suffix = "nd";
            break;
        case 3:
        case 23:
            suffix = "rd";
            break;
    }
    return date + suffix;
}
rohimsh
  • 211
  • 1
  • 9
  • Thanks for wanting to contribute. Is there any essential difference from [the existing asnwer by sivag 1](https://stackoverflow.com/a/28528293/5772882)? – Ole V.V. Mar 31 '21 at 08:58
  • Hey @OleV.V. No, there's no fundamental difference as such. However I felt this to be cleaner and understandable better. But that can be my perspective. If the community doesn't see any value, I can remove it. – rohimsh Apr 12 '21 at 14:24
0

In kotlin you can use like this

fun changeDateFormats(currentFormat: String, dateString: String): String {
        var result = ""
        try {
            val formatterOld = SimpleDateFormat(currentFormat, Locale.getDefault())
            formatterOld.timeZone = TimeZone.getTimeZone("UTC")

            var date: Date? = null

            date = formatterOld.parse(dateString)

            val dayFormate = SimpleDateFormat("d", Locale.getDefault())
            var day = dayFormate.format(date)

            val formatterNew = SimpleDateFormat("hh:mm a, d'" + getDayOfMonthSuffix(day.toInt()) + "' MMM yy", Locale.getDefault())

            if (date != null) {
                result = formatterNew.format(date)
            }

        } catch (e: ParseException) {
            e.printStackTrace()
            return dateString
        }

        return result
    }


    private fun getDayOfMonthSuffix(n: Int): String {
        if (n in 11..13) {
            return "th"
        }
        when (n % 10) {
            1 -> return "st"
            2 -> return "nd"
            3 -> return "rd"
            else -> return "th"
        }
    }

set like this

  txt_chat_time_me.text = changeDateFormats("SERVER_DATE", "DATE")
Mohit Suthar
  • 8,725
  • 10
  • 37
  • 67
-3

The following method can be used to get the formatted string of the date which is passed in to it. It'll format the date to say 1st,2nd,3rd,4th .. using SimpleDateFormat in Java. eg:- 1st of September 2015

public String getFormattedDate(Date date){
            Calendar cal=Calendar.getInstance();
            cal.setTime(date);
            //2nd of march 2015
            int day=cal.get(Calendar.DATE);

            switch (day % 10) {
            case 1:  
                return new SimpleDateFormat("d'st' 'of' MMMM yyyy").format(date);
            case 2:  
                return new SimpleDateFormat("d'nd' 'of' MMMM yyyy").format(date);
            case 3:  
                return new SimpleDateFormat("d'rd' 'of' MMMM yyyy").format(date);
            default: 
                return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
        }
Nadeeshani
  • 493
  • 4
  • 8
-4

The following is a more efficient answer to the question rather than hard-coding the style.

To change the day to ordinal number you need to use the following suffix.

DD +     TH = DDTH  result >>>> 4TH

OR to spell the number add SP to the format

DD + SPTH = DDSPTH   result >>> FOURTH

Find my completed answer in this question.

Community
  • 1
  • 1
J888
  • 1,944
  • 8
  • 42
  • 76
  • Question is to format in Java not in Oracle database http://docs.oracle.com/cd/B12037_01/server.101/b10759/sql_elements004.htm#BABGDDFB Java use SimpleDateFormat for date: https://docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html – Belal mazlom Aug 25 '15 at 10:50
-9
public String getDaySuffix(int inDay)
{
  String s = String.valueOf(inDay);

  if (s.endsWith("1"))
  {
    return "st";
  }
  else if (s.endsWith("2"))
  {
    return "nd";
  }
  else if (s.endsWith("3"))
  {
    return "rd";
  }
  else
  {
    return "th";
  }
}