By Ken Hume

2010-10-24 23:56:32 8 Comments

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?


@MEGHA DOBARIYA 2020-01-27 06:18:17

Try below function:

public static String getFormattedDate(Date date) 
  Calendar cal = Calendar.getInstance();
  //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);
     return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
  return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);

@David Buck 2020-01-27 06:42:59

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.

@Ole V.V. 2020-01-27 19:10:50

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. Also I’m in doubt what new your answer contributes compared to the other 18 answers?

@tbigby 2020-01-20 03:29:30

RuleBasedNumberFormat in ICU library

I appreciated the link to the ICU project's library from @Pierre-Olivier Dybman (, 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,
ZonedDateTime zdt ="Pacific/Auckland"));

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

Example output:





@Ole V.V. 2018-05-16 11:32:11

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.


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")

    LocalDate date = LocalDate.of(2018, Month.AUGUST, 30);
    for (int i = 0; i < 6; i++) {
        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 ="America/Menominee")).plusWeeks(1);
                        + ordinalIndicators[today.getDayOfMonth()]);

Running this snippet just now I got


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.

@Krease 2018-11-20 18:47:23

Would love to get this answer bumped up higher as this more modern answer is better than trying to roll your own implementation.

@C.A.B. 2014-09-26 17:12:34

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.

@Mad Physicist 2018-01-25 01:22:37

Good point about genders in Russian, but that would technically make %MMM impossible without additional context.

@C.A.B. 2018-12-15 21:43:34

@Mad Physicist, this is not true as %MMM will be applied to month, so we know the noun to conjugate.

@SamHoque 2018-12-12 10:04:09

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 =;//For reference
int month = localDate.getDayOfMonth();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(getPattern(month));
String date = localDate.format(formatter);

the output is

December 12th, 2018

@Mikkel Larsen 2019-02-07 11:18:52

This requires a minimum API of 26 though :)

@SamHoque 2019-02-09 06:06:21

@MikkelLarsen this question wasn't about android, I was using Java 8 for this. Android doesn't support a large api for java 8.

@Mikkel Larsen 2019-02-11 09:11:25

@SamSakerz my bad :)

@Basil Bourque 2020-01-20 04:43:09

@MikkelLarsen Most java.time functionality is back-ported to Java 6 & Java 7 in the ThreeTen-Backport project. Further adapted for earlier Android (<26) in ThreeTenABP. See How to use ThreeTenABP….

@tazmaniax 2018-11-08 07:08:48

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

@Ole V.V. 2018-11-09 03:06:55

Creative and convoluted. It certainly took me a long time to understand how it works. That’s not a sign of good code.

@tazmaniax 2018-11-12 06:32:36

@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

@Ole V.V. 2018-11-12 07:00:57

Very nice account of pros and cons. You may want to include it in the answer? Just an idea.

@ocodo 2010-10-25 01:26:52

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)];

@Sarz 2015-11-05 09:22:46

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();
            //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);
                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();
        for(int i=0;i<32;i++){


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

@Mohit Suthar 2018-02-13 07:12:33

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) {
            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")

@Pierre-Olivier Dybman 2017-10-18 14:47:13

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.

@Greg Mattes 2010-10-25 00:48:10

import static*;

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).

@Larry Lustig 2010-10-25 00:52:29

Yeah, but @kaliatech can fix his table whereas your algorithm cannot be adjusted to correctly handle the 11th, 12th and 13th of the month.

@stevevls 2011-12-11 14:11:55

it really feels like an oversight in the simple data format, doesn't it?

@Greg Mattes 2013-08-01 16:05:08

@J888 The ordinal-suffix style is hardcoded, but that's exactly the context of this question.

@Trejkaz 2014-05-20 05:49:47

Have fun internationalising this. :D

@Greg Mattes 2014-05-23 23:45:28

@Trejkaz Outside of the scope of the question :)

@Trejkaz 2014-05-24 00:12:10

@GregMattes who said it was in scope? It's something which always comes up eventually. (If the software is decent.)

@Gabe Sechan 2015-03-05 00:30:09

This answer needs to be downvoted to oblivion- it will horribly fail in any language other than english

@user1841702 2016-12-19 09:45:18

What if we want to do this for a different locale ?

@Dávid Horváth 2017-01-26 17:08:05

Chuck Norris can check bounds of an argument without Preconditions.

@Дмитро Яковлєв 2018-04-04 10:06:52

This solution supports only English. Use RuleBasedNumberFormat from ICU4J for the localized solution.

@SantiBailors 2018-04-04 14:16:32

@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?

@Trejkaz 2018-04-05 00:00:03

@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.

@SantiBailors 2018-04-05 05:43:45

@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).

@Greg Mattes 2018-04-07 13:56:39

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.

@kaliatech 2010-10-25 00:09:15

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();
 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" };

@Thorbjørn Ravn Andersen 2010-10-25 02:59:44

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.

@Nadeeshani 2015-09-02 04:00:08

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();
            //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);
                return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);

@sivag1 2015-02-15 16:29:24

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";

                    return "" + date + "th";

@J888 2013-07-31 03:12:37

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.

@Belal mazlom 2015-08-25 10:50:59

Question is to format in Java not in Oracle database… Java use SimpleDateFormat for date:…

@Talisphere 2013-02-26 20:26:48

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";
        return "th";

@SatanEnglish 2014-01-30 21:58:30

In what case would a Day Month sequence have more than 31 days??

@Jared Rummler 2015-05-15 23:31:20

@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. :)

@TheIT 2015-06-08 18:37:33

This method returns st for 11, nd for 12 and rd for 13

@Mad Physicist 2018-01-25 01:25:46

@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.

@Ole V.V. 2018-11-09 03:14:07

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.

@Simeon Hearring 2013-01-14 04:37:58

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";
    return "th";

@Rahul Sharma 2016-10-24 05:27:35

It should not use endsWith(); which produce wrong output

@Anand Shekhar 2011-12-12 12:40: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";
        return "th";

@typical danny 2020-05-30 09:47:28

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."

@Erdem Memisyazici 2011-08-08 19:58:13

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(,;
    return dateString;


@Basil Bourque 2015-10-28 18:38:59

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.

Related Questions

Sponsored Content

30 Answered Questions

[SOLVED] How to get the last day of the month?

  • 2008-09-04 00:54:44
  • Cristian
  • 357811 View
  • 646 Score
  • 30 Answer
  • Tags:   python date

26 Answered Questions

[SOLVED] In Java, what is the best way to determine the size of an object?

  • 2008-09-09 17:07:03
  • Jay R.
  • 294813 View
  • 623 Score
  • 26 Answer
  • Tags:   java memory

27 Answered Questions

[SOLVED] How to find the last day of the month from date?

  • 2009-11-06 10:27:50
  • Mithun Sreedharan
  • 464235 View
  • 388 Score
  • 27 Answer
  • Tags:   php date

44 Answered Questions

[SOLVED] Can't start Eclipse - Java was started but returned exit code=13

40 Answered Questions

[SOLVED] Difference in months between two dates

  • 2011-01-09 11:59:57
  • Rauf
  • 308213 View
  • 336 Score
  • 40 Answer
  • Tags:   c# .net date

3 Answered Questions

9 Answered Questions

[SOLVED] how to get the first and last days of a given month

  • 2011-09-24 21:08:11
  • dole doug
  • 92376 View
  • 70 Score
  • 9 Answer
  • Tags:   php date

4 Answered Questions

6 Answered Questions

[SOLVED] Is there a date format to display the day of the week in java?

1 Answered Questions

Sponsored Content