<p>Is there an easy way in C# to create <a href="http://en.wikipedia.org/wiki/Ordinal_numbers_%28linguistics%29" rel="noreferrer">Ordinals</a> for a number? For example:</p>

<ul>
<li>1 returns 1st</li>
<li>2 returns 2nd</li>
<li>3 returns 3rd</li>
<li>...etc</li>
</ul>

<p>Can this be done through <code>String.Format()</code> or are there any functions available to do this?</p>

[{'answer_id': 20166, 'author': 'Stu', 'author_id': 414, 'author_profile': 'https://Stackoverflow.com/users/414', 'pm_score': 4, 'selected': False, 'text': '<p>You\'ll have to roll your own. From the top of my head:</p>\n\n<pre><code>public static string Ordinal(this int number)\n{\n  var work = number.ToString();\n  if ((number % 100) == 11 || (number % 100) == 12 || (number % 100) == 13)\n    return work + "th";\n  switch (number % 10)\n  {\n    case 1: work += "st"; break;\n    case 2: work += "nd"; break;\n    case 3: work += "rd"; break;\n    default: work += "th"; break;\n  }\n  return work;\n}\n</code></pre>\n\n<p>You can then do </p>\n\n<pre><code>Console.WriteLine(432.Ordinal());\n</code></pre>\n\n<p>Edited for 11/12/13 exceptions. I DID say from the top of my head  :-)</p>\n\n<p>Edited for 1011 -- others have fixed this already, just want to make sure others don\'t grab this incorrect version.</p>\n'}, {'answer_id': 20175, 'author': 'samjudson', 'author_id': 1908, 'author_profile': 'https://Stackoverflow.com/users/1908', 'pm_score': 9, 'selected': True, 'text': '<p>This page gives you a complete listing of all custom numerical formatting rules:</p>\n<p><a href="https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings" rel="noreferrer">Custom numeric format strings</a></p>\n<p>As you can see, there is nothing in there about ordinals, so it can\'t be done using <code>String.Format</code>. However its not really that hard to write a function to do it.</p>\n<pre><code>public static string AddOrdinal(int num)\n{\n    if( num &lt;= 0 ) return num.ToString();\n\n    switch(num % 100)\n    {\n        case 11:\n        case 12:\n        case 13:\n            return num + &quot;th&quot;;\n    }\n    \n    switch(num % 10)\n    {\n        case 1:\n            return num + &quot;st&quot;;\n        case 2:\n            return num + &quot;nd&quot;;\n        case 3:\n            return num + &quot;rd&quot;;\n        default:\n            return num + &quot;th&quot;;\n    }\n}\n</code></pre>\n<p>Update: Technically Ordinals don\'t exist for &lt;= 0, so I\'ve updated the code above. Also removed the redundant <code>ToString()</code> methods.</p>\n<p>Also note, this is not internationalized. I\'ve no idea what ordinals look like in other languages.</p>\n'}, {'answer_id': 31066, 'author': 'Jesse C. Slicer', 'author_id': 3312, 'author_profile': 'https://Stackoverflow.com/users/3312', 'pm_score': 4, 'selected': False, 'text': '<p>I rather liked elements from both <a href="https://stackoverflow.com/a/20166/3312">Stu</a>\'s and <a href="https://stackoverflow.com/a/20175/3312">samjudson</a>\'s solutions and worked them together into what I think is a usable combo:</p>\n<pre><code>public static string Ordinal(this int number)\n{\n    const string TH = &quot;th&quot;;\n    var s = number.ToString();\n    \n    number %= 100;\n    \n    if ((number &gt;= 11) &amp;&amp; (number &lt;= 13))\n    {\n        return s + TH;\n    }\n   \n    switch (number % 10)\n    {\n        case 1:\n            return s + &quot;st&quot;;\n        case 2:\n            return s + &quot;nd&quot;;\n        case 3:\n            return s + &quot;rd&quot;;\n        default:\n            return s + TH;\n    }\n}\n</code></pre>\n'}, {'answer_id': 111542, 'author': 'Ryan McGeary', 'author_id': 8985, 'author_profile': 'https://Stackoverflow.com/users/8985', 'pm_score': 3, 'selected': False, 'text': '<p>While I haven\'t benchmarked this yet, you should be able to get better performance by avoiding all the conditional case statements.</p>\n\n<p>This is java, but a port to C# is trivial:</p>\n\n<pre><code>public class NumberUtil {\n  final static String[] ORDINAL_SUFFIXES = {\n    "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"\n  };\n\n  public static String ordinalSuffix(int value) {\n    int n = Math.abs(value);\n    int lastTwoDigits = n % 100;\n    int lastDigit = n % 10;\n    int index = (lastTwoDigits &gt;= 11 &amp;&amp; lastTwoDigits &lt;= 13) ? 0 : lastDigit;\n    return ORDINAL_SUFFIXES[index];\n  }\n\n  public static String toOrdinal(int n) {\n    return new StringBuffer().append(n).append(ordinalSuffix(n)).toString();\n  }\n}\n</code></pre>\n\n<p>Note, the reduction of conditionals and the use of the array lookup should speed up performance if generating a lot of ordinals in a tight loop.  However, I also concede that this isn\'t as readable as the case statement solution.</p>\n'}, {'answer_id': 115438, 'author': 'roomaroo', 'author_id': 3464, 'author_profile': 'https://Stackoverflow.com/users/3464', 'pm_score': 6, 'selected': False, 'text': '<p>Remember internationalisation!</p>\n\n<p>The solutions here only work for English. Things get a lot more complex if you need to support other languages. </p>\n\n<p>For example, in Spanish "1st" would be written as "1.o", "1.a", "1.os" or "1.as" depending on whether the thing you\'re counting is masculine, feminine or plural!</p>\n\n<p>So if your software needs to support different languages, try to avoid ordinals.</p>\n'}, {'answer_id': 620504, 'author': 'si618', 'author_id': 44540, 'author_profile': 'https://Stackoverflow.com/users/44540', 'pm_score': 5, 'selected': False, 'text': "<p>My version of Jesse's version of Stu's and samjudson's versions :)</p>\n<p>Included unit test to show that the accepted answer is incorrect when number &lt; 1</p>\n<pre><code>/// &lt;summary&gt;\n/// Get the ordinal value of positive integers.\n/// &lt;/summary&gt;\n/// &lt;remarks&gt;\n/// Only works for english-based cultures.\n/// Code from: http://stackoverflow.com/questions/20156/is-there-a-quick-way-to-create-ordinals-in-c/31066#31066\n/// With help: http://www.wisegeek.com/what-is-an-ordinal-number.htm\n/// &lt;/remarks&gt;\n/// &lt;param name=&quot;number&quot;&gt;The number.&lt;/param&gt;\n/// &lt;returns&gt;Ordinal value of positive integers, or &lt;see cref=&quot;int.ToString&quot;/&gt; if less than 1.&lt;/returns&gt;\npublic static string Ordinal(this int number)\n{\n    const string TH = &quot;th&quot;;\n    string s = number.ToString();\n\n    // Negative and zero have no ordinal representation\n    if (number &lt; 1)\n    {\n        return s;\n    }\n\n    number %= 100;\n    if ((number &gt;= 11) &amp;&amp; (number &lt;= 13))\n    {\n        return s + TH;\n    }\n\n    switch (number % 10)\n    {\n        case 1: return s + &quot;st&quot;;\n        case 2: return s + &quot;nd&quot;;\n        case 3: return s + &quot;rd&quot;;\n        default: return s + TH;\n    }\n}\n\n[Test]\npublic void Ordinal_ReturnsExpectedResults()\n{\n    Assert.AreEqual(&quot;-1&quot;, (1-2).Ordinal());\n    Assert.AreEqual(&quot;0&quot;, 0.Ordinal());\n    Assert.AreEqual(&quot;1st&quot;, 1.Ordinal());\n    Assert.AreEqual(&quot;2nd&quot;, 2.Ordinal());\n    Assert.AreEqual(&quot;3rd&quot;, 3.Ordinal());\n    Assert.AreEqual(&quot;4th&quot;, 4.Ordinal());\n    Assert.AreEqual(&quot;5th&quot;, 5.Ordinal());\n    Assert.AreEqual(&quot;6th&quot;, 6.Ordinal());\n    Assert.AreEqual(&quot;7th&quot;, 7.Ordinal());\n    Assert.AreEqual(&quot;8th&quot;, 8.Ordinal());\n    Assert.AreEqual(&quot;9th&quot;, 9.Ordinal());\n    Assert.AreEqual(&quot;10th&quot;, 10.Ordinal());\n    Assert.AreEqual(&quot;11th&quot;, 11.Ordinal());\n    Assert.AreEqual(&quot;12th&quot;, 12.Ordinal());\n    Assert.AreEqual(&quot;13th&quot;, 13.Ordinal());\n    Assert.AreEqual(&quot;14th&quot;, 14.Ordinal());\n    Assert.AreEqual(&quot;20th&quot;, 20.Ordinal());\n    Assert.AreEqual(&quot;21st&quot;, 21.Ordinal());\n    Assert.AreEqual(&quot;22nd&quot;, 22.Ordinal());\n    Assert.AreEqual(&quot;23rd&quot;, 23.Ordinal());\n    Assert.AreEqual(&quot;24th&quot;, 24.Ordinal());\n    Assert.AreEqual(&quot;100th&quot;, 100.Ordinal());\n    Assert.AreEqual(&quot;101st&quot;, 101.Ordinal());\n    Assert.AreEqual(&quot;102nd&quot;, 102.Ordinal());\n    Assert.AreEqual(&quot;103rd&quot;, 103.Ordinal());\n    Assert.AreEqual(&quot;104th&quot;, 104.Ordinal());\n    Assert.AreEqual(&quot;110th&quot;, 110.Ordinal());\n    Assert.AreEqual(&quot;111th&quot;, 111.Ordinal());\n    Assert.AreEqual(&quot;112th&quot;, 112.Ordinal());\n    Assert.AreEqual(&quot;113th&quot;, 113.Ordinal());\n    Assert.AreEqual(&quot;114th&quot;, 114.Ordinal());\n    Assert.AreEqual(&quot;120th&quot;, 120.Ordinal());\n    Assert.AreEqual(&quot;121st&quot;, 121.Ordinal());\n    Assert.AreEqual(&quot;122nd&quot;, 122.Ordinal());\n    Assert.AreEqual(&quot;123rd&quot;, 123.Ordinal());\n    Assert.AreEqual(&quot;124th&quot;, 124.Ordinal());\n}\n</code></pre>\n"}, {'answer_id': 16339730, 'author': 'shawad', 'author_id': 1765067, 'author_profile': 'https://Stackoverflow.com/users/1765067', 'pm_score': 2, 'selected': False, 'text': '<p>Similar to Ryan\'s solution, but even more basic, I just use a plain array and use the day to look up the correct ordinal:</p>\n\n<pre><code>private string[] ordinals = new string[] {"","st","nd","rd","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","st","nd","rd","th","th","th","th","th","th","th","st" };\nDateTime D = DateTime.Now;\nString date = "Today\'s day is: "+ D.Day.ToString() + ordinals[D.Day];\n</code></pre>\n\n<p>I have not had the need, but I would assume you could use a multidimensional array if you wanted to have multiple language support.</p>\n\n<p>From what I can remember from my Uni days, this method requires minimal effort from the server.</p>\n'}, {'answer_id': 20610493, 'author': 'Faust', 'author_id': 613004, 'author_profile': 'https://Stackoverflow.com/users/613004', 'pm_score': 1, 'selected': False, 'text': '<pre><code>public static string OrdinalSuffix(int ordinal)\n{\n    //Because negatives won\'t work with modular division as expected:\n    var abs = Math.Abs(ordinal); \n\n    var lastdigit = abs % 10; \n\n    return \n        //Catch 60% of cases (to infinity) in the first conditional:\n        lastdigit &gt; 3 || lastdigit == 0 || (abs % 100) - lastdigit == 10 ? "th" \n            : lastdigit == 1 ? "st" \n            : lastdigit == 2 ? "nd" \n            : "rd";\n}\n</code></pre>\n'}, {'answer_id': 21968095, 'author': 'Rupert', 'author_id': 1904153, 'author_profile': 'https://Stackoverflow.com/users/1904153', 'pm_score': -1, 'selected': False, 'text': '<p>Another alternative that I used based on all the other suggestions, but requires no special casing:</p>\n<pre><code>public static string DateSuffix(int day)\n{\n    if (day == 11 | day == 12 | day == 13) return &quot;th&quot;;\n    Math.DivRem(day, 10, out day);\n    switch (day)\n    {\n        case 1:\n            return &quot;st&quot;;\n        case 2:\n            return &quot;nd&quot;;\n        case 3:\n            return &quot;rd&quot;;\n        default:\n            return &quot;th&quot;;\n    }\n}\n</code></pre>\n'}, {'answer_id': 25935496, 'author': 'AjV Jsy', 'author_id': 2078245, 'author_profile': 'https://Stackoverflow.com/users/2078245', 'pm_score': 0, 'selected': False, 'text': '<p>FWIW, for MS-SQL, this expression will do the job. Keep the first WHEN (<code>WHEN num % 100 IN (11, 12, 13) THEN \'th\'</code>) as the first one in the list, as this relies upon being tried before the others.</p>\n\n<pre><code>CASE\n  WHEN num % 100 IN (11, 12, 13) THEN \'th\' -- must be tried first\n  WHEN num % 10 = 1 THEN \'st\'\n  WHEN num % 10 = 2 THEN \'nd\'\n  WHEN num % 10 = 3 THEN \'rd\'\n  ELSE \'th\'\nEND AS Ordinal\n</code></pre>\n\n<p>For Excel :</p>\n\n<pre><code>=MID("thstndrdth",MIN(9,2*RIGHT(A1)*(MOD(A1-11,100)&gt;2)+1),2)\n</code></pre>\n\n<p>The expression  <code>(MOD(A1-11,100)&gt;2)</code> is TRUE (1) for all numbers except any ending in <code>11,12,13</code> (FALSE = 0). So <code>2 * RIGHT(A1) * (MOD(A1-11,100)&gt;2) +1)</code> ends up as 1 for 11/12/13, otherwise :<br>\n1 evaluates to 3<br>\n2 to 5,<br>\n3 to 7<br>\nothers : 9<br>\n - and the required 2 characters are selected from <code>"thstndrdth"</code> starting from that position.</p>\n\n<p>If you really want to convert that fairly directly to SQL, this worked for me for a handful of test values :</p>\n\n<pre><code>DECLARE @n as int\nSET @n=13\nSELECT SubString(  \'thstndrdth\'\n                 , (SELECT MIN(value) FROM\n                     (SELECT 9 as value UNION\n                      SELECT 1+ (2* (ABS(@n) % 10)  *  CASE WHEN ((ABS(@n)+89) % 100)&gt;2 THEN 1 ELSE 0 END)\n                     ) AS Mins\n                   )\n                 , 2\n                )\n</code></pre>\n'}, {'answer_id': 26282053, 'author': 'Maulik Patel', 'author_id': 4125994, 'author_profile': 'https://Stackoverflow.com/users/4125994', 'pm_score': -1, 'selected': False, 'text': "<p>Here is the DateTime Extension class. Copy, Paste &amp; Enjoy</p>\n<pre><code>public static class DateTimeExtensions\n{\n    public static string ToStringWithOrdinal(this DateTime d)\n    {\n        var result = &quot;&quot;;\n        bool bReturn = false;            \n        \n        switch (d.Day % 100)\n        {\n            case 11:\n            case 12:\n            case 13:\n                result = d.ToString(&quot;dd'th' MMMM yyyy&quot;);\n                bReturn = true;\n                break;\n        }\n\n        if (!bReturn)\n        {\n            switch (d.Day % 10)\n            {\n                case 1:\n                    result = d.ToString(&quot;dd'st' MMMM yyyy&quot;);\n                    break;\n                case 2:\n                    result = d.ToString(&quot;dd'nd' MMMM yyyy&quot;);\n                    break;\n                case 3:\n                    result = d.ToString(&quot;dd'rd' MMMM yyyy&quot;);\n                    break;\n                default:\n                    result = d.ToString(&quot;dd'th' MMMM yyyy&quot;);\n                    break;\n            }\n\n        }\n\n        if (result.StartsWith(&quot;0&quot;)) result = result.Substring(1);\n        return result;\n    }\n}\n</code></pre>\n<p>Result :</p>\n<p>9th October 2014</p>\n"}, {'answer_id': 26433514, 'author': 'Shahzad Qureshi', 'author_id': 2719563, 'author_profile': 'https://Stackoverflow.com/users/2719563', 'pm_score': 5, 'selected': False, 'text': '<p>Simple, clean, quick</p>\n<pre><code>private static string GetOrdinalSuffix(int num)\n{\n    string number = num.ToString();\n    if (number.EndsWith(&quot;11&quot;)) return &quot;th&quot;;\n    if (number.EndsWith(&quot;12&quot;)) return &quot;th&quot;;\n    if (number.EndsWith(&quot;13&quot;)) return &quot;th&quot;;\n    if (number.EndsWith(&quot;1&quot;)) return &quot;st&quot;;\n    if (number.EndsWith(&quot;2&quot;)) return &quot;nd&quot;;\n    if (number.EndsWith(&quot;3&quot;)) return &quot;rd&quot;;\n    return &quot;th&quot;;\n}\n</code></pre>\n<p>Or better yet, as an extension method</p>\n<pre><code>public static class IntegerExtensions\n{\n    public static string DisplayWithSuffix(this int num)\n    {\n        string number = num.ToString();\n        if (number.EndsWith(&quot;11&quot;)) return number + &quot;th&quot;;\n        if (number.EndsWith(&quot;12&quot;)) return number + &quot;th&quot;;\n        if (number.EndsWith(&quot;13&quot;)) return number + &quot;th&quot;;\n        if (number.EndsWith(&quot;1&quot;)) return number + &quot;st&quot;;\n        if (number.EndsWith(&quot;2&quot;)) return number + &quot;nd&quot;;\n        if (number.EndsWith(&quot;3&quot;)) return number + &quot;rd&quot;;\n        return number + &quot;th&quot;;\n    }\n}\n</code></pre>\n<p>Now you can just call</p>\n<pre><code>int a = 1;\na.DisplayWithSuffix(); \n</code></pre>\n<p>or even as direct as</p>\n<pre><code>1.DisplayWithSuffix();\n</code></pre>\n'}, {'answer_id': 27509908, 'author': 'Whelkaholism', 'author_id': 318411, 'author_profile': 'https://Stackoverflow.com/users/318411', 'pm_score': 1, 'selected': False, 'text': '<p><strong>EDIT</strong>: As YM_Industries points out in the comment, <a href="https://stackoverflow.com/questions/20156/is-there-an-easy-way-to-create-ordinals-in-c/20175#20175" title="accepted answer">samjudson\'s answer</a> DOES work for numbers over 1000, nickf\'s comment seems to have gone, and I can\'t remember what the problem I saw was. Left this answer here for the comparison timings.</p>\n\n<p>An awful lot of these don\'t work for numbers > 999, as <a href="https://stackoverflow.com/users/9021/nickf" title="nickf">nickf</a> pointed out in a comment (EDIT: now missing).</p>\n\n<p>Here is a version based off a modified version of <a href="https://stackoverflow.com/users/1908/samjudson" title="samjudson">samjudson</a>\'s <a href="https://stackoverflow.com/questions/20156/is-there-an-easy-way-to-create-ordinals-in-c/20175#20175" title="accepted answer">accepted answer</a> that does.</p>\n\n<pre><code>public static String GetOrdinal(int i)\n{\n    String res = "";\n\n    if (i &gt; 0)\n    {\n        int j = (i - ((i / 100) * 100));\n\n        if ((j == 11) || (j == 12) || (j == 13))\n            res = "th";\n        else\n        {\n            int k = i % 10;\n\n            if (k == 1)\n                res = "st";\n            else if (k == 2)\n                res = "nd";\n            else if (k == 3)\n                res = "rd";\n            else\n                res = "th";\n        }\n    }\n\n    return i.ToString() + res;\n}\n</code></pre>\n\n<p>Also <a href="https://stackoverflow.com/users/2719563/shahzad-qureshi" title="Shahzad Qureshi">Shahzad Qureshi</a>\'s <a href="https://stackoverflow.com/questions/69262/is-there-an-easy-way-in-net-to-get-st-nd-rd-and-th-endings-for-number/19553611#19553611">answer</a> using string manipulation works fine, however it does have a performance penalty. For generating a lot of these, a LINQPad example program makes the string version 6-7 times slower than this integer one (although you\'d have to be generating a lot to notice).</p>\n\n<p>LINQPad example: </p>\n\n<pre><code>void Main()\n{\n    "Examples:".Dump();\n\n    foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 10000013 })\n        Stuff.GetOrdinal(i).Dump();\n\n    String s;\n\n    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();\n\n    for(int iter = 0; iter &lt; 100000; iter++)\n        foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 1000013 })\n            s = Stuff.GetOrdinal(i);\n\n    "Integer manipulation".Dump();\n    sw.Elapsed.Dump();\n\n    sw.Restart();\n\n    for(int iter = 0; iter &lt; 100000; iter++)\n        foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 1000013 })\n            s = (i.ToString() + Stuff.GetOrdinalSuffix(i));\n\n    "String manipulation".Dump();\n    sw.Elapsed.Dump();\n}\n\npublic class Stuff\n{\n        // Use integer manipulation\n        public static String GetOrdinal(int i)\n        {\n                String res = "";\n\n                if (i &gt; 0)\n                {\n                        int j = (i - ((i / 100) * 100));\n\n                        if ((j == 11) || (j == 12) || (j == 13))\n                                res = "th";\n                        else\n                        {\n                                int k = i % 10;\n\n                                if (k == 1)\n                                        res = "st";\n                                else if (k == 2)\n                                        res = "nd";\n                                else if (k == 3)\n                                        res = "rd";\n                                else\n                                        res = "th";\n                        }\n                }\n\n                return i.ToString() + res;\n        }\n\n        // Use string manipulation\n        public static string GetOrdinalSuffix(int num)\n        {\n                if (num.ToString().EndsWith("11")) return "th";\n                if (num.ToString().EndsWith("12")) return "th";\n                if (num.ToString().EndsWith("13")) return "th";\n                if (num.ToString().EndsWith("1")) return "st";\n                if (num.ToString().EndsWith("2")) return "nd";\n                if (num.ToString().EndsWith("3")) return "rd";\n                return "th";\n        }\n}\n</code></pre>\n'}, {'answer_id': 36878179, 'author': 'Ali Humayun', 'author_id': 1845464, 'author_profile': 'https://Stackoverflow.com/users/1845464', 'pm_score': 2, 'selected': False, 'text': '<pre><code>private static string GetOrd(int num) =&gt; $&quot;{num}{(!(Range(11, 3).Any(n =&gt; n == num % 100) ^ Range(1, 3).All(n =&gt; n != num % 10)) ? new[] { &quot;ˢᵗ&quot;, &quot;ⁿᵈ&quot;, &quot;ʳᵈ&quot; }[num % 10 - 1] : &quot;ᵗʰ&quot;)}&quot;;\n</code></pre>\n<p>If anyone is looking for one-liner.</p>\n'}, {'answer_id': 39665303, 'author': 'Perry Tribolet', 'author_id': 5668, 'author_profile': 'https://Stackoverflow.com/users/5668', 'pm_score': 2, 'selected': False, 'text': '<p>I use this extension class:</p>\n\n<pre><code>public static class Int32Extensions\n{\n    public static string ToOrdinal(this int i)\n    {\n        return (i + "th")\n            .Replace("1th", "1st")\n            .Replace("2th", "2nd")\n            .Replace("3th", "3rd");\n    }\n}\n</code></pre>\n'}, {'answer_id': 43260913, 'author': 'Ian Warburton', 'author_id': 221683, 'author_profile': 'https://Stackoverflow.com/users/221683', 'pm_score': 2, 'selected': False, 'text': '<p>Requested "less redundancy" version of samjudson\'s answer...</p>\n\n<pre><code>public static string AddOrdinal(int number)\n{\n    if (number &lt;= 0) return number.ToString();\n\n    string GetIndicator(int num)\n    {\n        switch (num % 100)\n        {\n            case 11:\n            case 12:\n            case 13:\n                return "th";\n        }\n\n        switch (num % 10)\n        {\n            case 1:\n                return "st";\n            case 2:\n                return "nd";\n            case 3:\n                return "rd";\n            default:\n                return "th";\n        }\n    }\n\n    return number + GetIndicator(number);\n}\n</code></pre>\n'}, {'answer_id': 44010138, 'author': 'Dave Sumter', 'author_id': 706866, 'author_profile': 'https://Stackoverflow.com/users/706866', 'pm_score': 1, 'selected': False, 'text': '<p>Based off the other answers:</p>\n\n<pre><code>public static string Ordinal(int n)\n{   \n    int     r = n % 100,     m = n % 10;\n\n    return (r&lt;4 || r&gt;20) &amp;&amp; (m&gt;0 &amp;&amp; m&lt;4) ? n+"  stndrd".Substring(m*2,2) : n+"th";                                              \n}\n</code></pre>\n'}, {'answer_id': 56439487, 'author': 'Phani Rithvij', 'author_id': 8608146, 'author_profile': 'https://Stackoverflow.com/users/8608146', 'pm_score': 0, 'selected': False, 'text': '<p>This is the implementation in <code>dart</code> and can be modified according to the language.</p>\n\n<pre class="lang-dart prettyprint-override"><code>String getOrdinalSuffix(int num){\n    if (num.toString().endsWith("11")) return "th";\n    if (num.toString().endsWith("12")) return "th";\n    if (num.toString().endsWith("13")) return "th";\n    if (num.toString().endsWith("1")) return "st";\n    if (num.toString().endsWith("2")) return "nd";\n    if (num.toString().endsWith("3")) return "rd";\n    return "th";\n}\n</code></pre>\n'}, {'answer_id': 58378465, 'author': 'Dan Dohotaru', 'author_id': 2583579, 'author_profile': 'https://Stackoverflow.com/users/2583579', 'pm_score': 1, 'selected': False, 'text': "<p>While there are plenty of good answers in here, I guess there is room for another one, this time based on pattern matching, if not for anything else, then at least for debatable readability</p>\n<pre><code>public static string Ordinals1(this int number)\n{\n    switch (number)\n    {\n        case int p when p % 100 == 11:\n        case int q when q % 100 == 12:\n        case int r when r % 100 == 13:\n            return $&quot;{number}th&quot;;\n        case int p when p % 10 == 1:\n            return $&quot;{number}st&quot;;\n        case int p when p % 10 == 2:\n            return $&quot;{number}nd&quot;;\n        case int p when p % 10 == 3:\n            return $&quot;{number}rd&quot;;\n        default:\n            return $&quot;{number}th&quot;;\n    }\n}\n</code></pre>\n<p>and what makes this solution special? nothing but the fact that I'm adding some performance considerations for various other solutions</p>\n<p>frankly I doubt performance really matters for this particular scenario (who really needs the ordinals of millions of numbers) but at least it surfaces some comparisons to be taken into account...</p>\n<blockquote>\n<p>1 million items for reference (your millage may vary based on machine specs of course)</p>\n<p>with pattern matching and divisions (this answer)</p>\n<p>~622 ms</p>\n<p>with pattern matching and strings (this answer)</p>\n<p>~1967 ms</p>\n<p>with two switches and divisions (accepted answer)</p>\n<p>~637 ms</p>\n<p>with one switch and divisions (another answer)</p>\n<p>~725 ms</p>\n</blockquote>\n<pre><code>void Main()\n{\n    var timer = new Stopwatch();\n    var numbers = Enumerable.Range(1, 1000000).ToList();\n\n    // 1\n    timer.Reset();\n    timer.Start();\n    var results1 = numbers.Select(p =&gt; p.Ordinals1()).ToList();\n    timer.Stop();\n    timer.Elapsed.TotalMilliseconds.Dump(&quot;with pattern matching and divisions&quot;);\n\n    // 2\n    timer.Reset();\n    timer.Start();\n    var results2 = numbers.Select(p =&gt; p.Ordinals2()).ToList();\n    timer.Stop();\n    timer.Elapsed.TotalMilliseconds.Dump(&quot;with pattern matching and strings&quot;);\n\n    // 3\n    timer.Reset();\n    timer.Start();\n    var results3 = numbers.Select(p =&gt; p.Ordinals3()).ToList();\n    timer.Stop();\n    timer.Elapsed.TotalMilliseconds.Dump(&quot;with two switches and divisons&quot;);\n    \n    // 4\n    timer.Reset();\n    timer.Start();\n    var results4 = numbers.Select(p =&gt; p.Ordinals4()).ToList();\n    timer.Stop();\n    timer.Elapsed.TotalMilliseconds.Dump(&quot;with one switche and divisons&quot;);\n}\n\npublic static class Extensions\n{\n    public static string Ordinals1(this int number)\n    {\n        switch (number)\n        {\n            case int p when p % 100 == 11:\n            case int q when q % 100 == 12:\n            case int r when r % 100 == 13:\n                return $&quot;{number}th&quot;;\n            case int p when p % 10 == 1:\n                return $&quot;{number}st&quot;;\n            case int p when p % 10 == 2:\n                return $&quot;{number}nd&quot;;\n            case int p when p % 10 == 3:\n                return $&quot;{number}rd&quot;;\n            default:\n                return $&quot;{number}th&quot;;\n        }\n    }\n\n    public static string Ordinals2(this int number)\n    {\n        var text = number.ToString();\n        switch (text)\n        {\n            case string p when p.EndsWith(&quot;11&quot;):\n                return $&quot;{number}th&quot;;\n            case string p when p.EndsWith(&quot;12&quot;):\n                return $&quot;{number}th&quot;;\n            case string p when p.EndsWith(&quot;13&quot;):\n                return $&quot;{number}th&quot;;\n            case string p when p.EndsWith(&quot;1&quot;):\n                return $&quot;{number}st&quot;;\n            case string p when p.EndsWith(&quot;2&quot;):\n                return $&quot;{number}nd&quot;;\n            case string p when p.EndsWith(&quot;3&quot;):\n                return $&quot;{number}rd&quot;;\n            default:\n                return $&quot;{number}th&quot;;\n        }\n    }\n\n    public static string Ordinals3(this int number)\n    {\n        switch (number % 100)\n        {\n            case 11:\n            case 12:\n            case 13:\n                return $&quot;{number}th&quot;;\n        }\n\n        switch (number % 10)\n        {\n            case 1:\n                return $&quot;{number}st&quot;;\n            case 2:\n                return $&quot;{number}nd&quot;;\n            case 3:\n                return $&quot;{number}rd&quot;;\n            default:\n                return $&quot;{number}th&quot;;\n        }\n    }\n\n    public static string Ordinals4(this int number)\n    {\n        var ones = number % 10;\n        var tens = Math.Floor(number / 10f) % 10;\n        if (tens == 1)\n        {\n            return $&quot;{number}th&quot;;\n        }\n\n        switch (ones)\n        {\n            case 1:\n                return $&quot;{number}th&quot;;\n            case 2:\n                return $&quot;{number}nd&quot;;\n            case 3:\n                return $&quot;{number}rd&quot;;\n            default:\n                return $&quot;{number}th&quot;;\n        }\n    }\n}\n\n</code></pre>\n"}, {'answer_id': 58632762, 'author': 'Pluto', 'author_id': 1507941, 'author_profile': 'https://Stackoverflow.com/users/1507941', 'pm_score': 0, 'selected': False, 'text': '<p>Another one-liner, but without comparisons by only indexing the regex result into an array.</p>\n\n<pre><code>public static string GetOrdinalSuffix(int input)\n{\n    return new []{"th", "st", "nd", "rd"}[Convert.ToInt32("0" + Regex.Match(input.ToString(), "(?&lt;!1)[1-3]$").Value)];\n}\n</code></pre>\n\n<p>The PowerShell version can be shortened further:</p>\n\n<pre><code>function ord($num) { return (\'th\',\'st\',\'nd\',\'rd\')[[int]($num -match \'(?&lt;!1)[1-3]$\') * $matches[0]] }\n</code></pre>\n'}, {'answer_id': 61644903, 'author': 'Echostorm', 'author_id': 12862, 'author_profile': 'https://Stackoverflow.com/users/12862', 'pm_score': 0, 'selected': False, 'text': '<p>Another 1 liner.</p>\n\n<pre><code>public static string Ordinal(this int n)\n{    \n return n + (new [] {"st","nd","rd" }.ElementAtOrDefault((((n + 90) % 100 - 10) % 10 - 1)) ?? "th");\n}\n</code></pre>\n'}, {'answer_id': 72025082, 'author': 'aloisdg', 'author_id': 1248177, 'author_profile': 'https://Stackoverflow.com/users/1248177', 'pm_score': 1, 'selected': False, 'text': '<p>The <a href="https://github.com/Humanizr/Humanizer" rel="nofollow noreferrer">Humanizer</a> nuget package will provide helper methods for you. Disclaimer, I am a contributor to this project.</p>\n<p><code>Ordinalize</code> turns a number into an ordinal string used to denote the position in an ordered sequence such as 1st, 2nd, 3rd, 4th:</p>\n<pre class="lang-cs prettyprint-override"><code>1.Ordinalize() =&gt; &quot;1st&quot;\n5.Ordinalize() =&gt; &quot;5th&quot;\n</code></pre>\n<p>You can also call <code>Ordinalize</code> on a numeric string and achieve the same result: <code>&quot;21&quot;.Ordinalize()</code> =&gt; <code>&quot;21st&quot;</code></p>\n<p><code>Ordinalize</code> also supports grammatical gender for both forms.\nYou can pass an argument to <code>Ordinalize</code> to specify which gender the number should be outputted in.\nThe possible values are <code>GrammaticalGender.Masculine</code>, <code>GrammaticalGender.Feminine</code> and <code>GrammaticalGender.Neuter</code>:</p>\n<pre class="lang-cs prettyprint-override"><code>// for Brazilian Portuguese locale\n1.Ordinalize(GrammaticalGender.Masculine) =&gt; &quot;1º&quot;\n1.Ordinalize(GrammaticalGender.Feminine) =&gt; &quot;1ª&quot;\n1.Ordinalize(GrammaticalGender.Neuter) =&gt; &quot;1º&quot;\n&quot;2&quot;.Ordinalize(GrammaticalGender.Masculine) =&gt; &quot;2º&quot;\n&quot;2&quot;.Ordinalize(GrammaticalGender.Feminine) =&gt; &quot;2ª&quot;\n&quot;2&quot;.Ordinalize(GrammaticalGender.Neuter) =&gt; &quot;2º&quot;\n</code></pre>\n<p>Obviously this only applies to some cultures. For others passing gender in or not passing at all doesn\'t make any difference in the result.</p>\n<p>In addition, <code>Ordinalize</code> supports variations some cultures apply depending on the position of the ordinalized number in a sentence.\nUse the argument <code>wordForm</code> to get one result or another. Possible values are <code>WordForm.Abbreviation</code> and <code>WordForm.Normal</code>.\nYou can combine <code>wordForm</code> argument with gender but passing this argument in when it is not applicable will not make any difference in the result.</p>\n<pre class="lang-cs prettyprint-override"><code>// Spanish locale\n1.Ordinalize(WordForm.Abbreviation) =&gt; &quot;1.er&quot; // As in &quot;Vivo en el 1.er piso&quot;\n1.Ordinalize(WordForm.Normal) =&gt; &quot;1.º&quot; // As in &quot;He llegado el 1º&quot;\n&quot;3&quot;.Ordinalize(GrammaticalGender.Feminine, WordForm.Abbreviation) =&gt; &quot;3.ª&quot;\n&quot;3&quot;.Ordinalize(GrammaticalGender.Feminine, WordForm.Normal) =&gt; &quot;3.ª&quot;\n&quot;3&quot;.Ordinalize(GrammaticalGender.Masculine, WordForm.Abbreviation) =&gt; &quot;3.er&quot;\n&quot;3&quot;.Ordinalize(GrammaticalGender.Masculine, WordForm.Normal) =&gt; &quot;3.º&quot;\n</code></pre>\n<p>If you want to go deeper, check those test cases: <a href="https://github.com/Humanizr/Humanizer/blob/60822b6ddbfef1097feb873fd0161dc812576fce/src/Humanizer.Tests.Shared/OrdinalizeTests.cs" rel="nofollow noreferrer">OrdinalizeTests.cs</a></p>\n'}, {'answer_id': 72792661, 'author': 'Grastveit', 'author_id': 470022, 'author_profile': 'https://Stackoverflow.com/users/470022', 'pm_score': 1, 'selected': False, 'text': '<p>The accepted answer with switch-expressions and pattern-matching from c# 8 and 9.</p>\n<p>No unneeded string conversion or allocations.</p>\n<pre class="lang-cs prettyprint-override"><code>string.Concat(number, number &lt; 0 ? &quot;&quot; : (number % 100) switch \n{   \n    11 or 12 or 13 =&gt; &quot;th&quot;, \n    int n =&gt; (n % 10) switch \n    { \n        1 =&gt; &quot;st&quot;, \n        2 =&gt; &quot;nd&quot;, \n        3 =&gt; &quot;rd&quot;, \n        _ =&gt; &quot;th&quot;, \n    }\n})\n</code></pre>\n<p>Or as unfriendly one-liner:</p>\n<pre class="lang-cs prettyprint-override"><code>$&quot;{number}{(number &lt; 0 ? &quot;&quot; : (number % 100) switch { 11 or 12 or 13 =&gt; &quot;th&quot;, int n =&gt; (n % 10) switch { 1 =&gt; &quot;st&quot;, 2 =&gt; &quot;nd&quot;, 3 =&gt; &quot;rd&quot;, _ =&gt; &quot;th&quot; }})}&quot;\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/20156', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/383/']