Existe uma maneira fácil de criar ordinais em c #?


202

Existe uma maneira fácil em C # de criar Ordinais para um número? Por exemplo:

  • 1 retorna 1º
  • 2 retorna 2º
  • 3 retorna 3
  • ... etc

Isso pode ser feito String.Format()ou existem funções disponíveis para fazer isso?

Respostas:


311

Esta página fornece uma lista completa de todas as regras de formatação numérica personalizadas:

http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

Como você pode ver, não há nada sobre ordinais, por isso não pode ser feito usando String.Format. No entanto, não é realmente tão difícil escrever uma função para fazê-lo.

public static string AddOrdinal(int num)
{
    if( num <= 0 ) return num.ToString();

    switch(num % 100)
    {
        case 11:
        case 12:
        case 13:
            return num + "th";
    }

    switch(num % 10)
    {
        case 1:
            return num + "st";
        case 2:
            return num + "nd";
        case 3:
            return num + "rd";
        default:
            return num + "th";
    }
}

Atualização: tecnicamente, os ordinais não existem para <= 0; por isso, atualizei o código acima. Também foram removidos os ToString()métodos redundantes .

Observe também que isso não é internacionalizado. Não tenho idéia de como são os ordinais em outros idiomas.


2
Assert.AreEqual ("0", AddOrdinal (0)); Veja wisegeek.com/what-is-an-ordinal-number.htm
si618

2
Usar um método de extensão (ou como ele é chamado - veja a resposta de @ Stu) funcionaria muito bem aqui. @ Si, Adicionar essa condição seria muito fácil se necessário.
226

12
Esqueceu sobre '11, 12, 13' ... deve ser uma pergunta da entrevista. :-)
Holf 13/03

2
Sim, os programadores também são estranhos;)
samjudson

2
@IanWarburton Não há redundância, pois apenas uma única declaração de retorno será atingida. Se você não estiver satisfeito com a resposta, forneça a sua, mostrando a maneira "correta" de fazer isso e por que isso importa.
B2K

73

Lembre-se da internacionalização!

As soluções aqui funcionam apenas em inglês. As coisas ficam muito mais complexas se você precisar oferecer suporte a outros idiomas.

Por exemplo, em espanhol "1º" seria escrito como "1.o", "1.a", "1.os" ou "1.as", dependendo se o que você está contando é masculino, feminino ou plural !

Portanto, se o seu software precisar suportar idiomas diferentes, tente evitar os ordinais.


7
@ Andomar: "Os 2 primeiros leitores" => em italiano (e também espanhol, suponho) "primeiro" é plural aqui. Então você tem singular masculino, singular feminino, plural masculino, plural feminino; talvez alguma linguagem tem também um caso neutro (distinguing coisas de homens / animais)
M.Turrini

2
Dito isto, você não precisa evitar os ordinais: inclua-os na localização, depois de conhecer todo o caso que pode enfrentar ou (faça seu cliente) aceitar algumas limitações.
M.Turrini

26
Isso explica por que a equipe .NET evitou adicionando-o aos formatadores de data e hora
Chris S

moment.js tem uma função de "ordinal" formatação com o local assim parece factível, também gostaria que eles pudessem ter feito isso em .NET para DateTime
Guillaume86

5
Tudo seria muito simples se todos vocês usassem o "." caractere para ordinais, como fazemos em alemão)))) 1. 2. 3. 4. 5., etc. Embora o algoritmo seja muito mais interessante se alguém escrever o número e precisar acrescentar inflexão em 4 casos gramaticais com 3 artigos diferentes, além dos casos singular e plural das 12 combinações diferentes. Pense bem: os russos não têm mais 2, mais o vocativo, e algumas línguas nórdicas têm 15, eu acho. Eu adoraria ver essa implementação no .NET.
111315 Stefan Steiger

22

Minha versão da versão de Jesse das versões de Stu e samjudson :)

Teste de unidade incluído para mostrar que a resposta aceita está incorreta quando o número <1

    /// <summary>
    /// Get the ordinal value of positive integers.
    /// </summary>
    /// <remarks>
    /// Only works for english-based cultures.
    /// Code from: http://stackoverflow.com/questions/20156/is-there-a-quick-way-to-create-ordinals-in-c/31066#31066
    /// With help: http://www.wisegeek.com/what-is-an-ordinal-number.htm
    /// </remarks>
    /// <param name="number">The number.</param>
    /// <returns>Ordinal value of positive integers, or <see cref="int.ToString"/> if less than 1.</returns>
    public static string Ordinal(this int number)
    {
        const string TH = "th";
        string s = number.ToString();

        // Negative and zero have no ordinal representation
        if (number < 1)
        {
            return s;
        }

        number %= 100;
        if ((number >= 11) && (number <= 13))
        {
            return s + TH;
        }

        switch (number % 10)
        {
            case 1: return s + "st";
            case 2: return s + "nd";
            case 3: return s + "rd";
            default: return s + TH;
        }
    }

    [Test]
    public void Ordinal_ReturnsExpectedResults()
    {
        Assert.AreEqual("-1", (1-2).Ordinal());
        Assert.AreEqual("0", 0.Ordinal());
        Assert.AreEqual("1st", 1.Ordinal());
        Assert.AreEqual("2nd", 2.Ordinal());
        Assert.AreEqual("3rd", 3.Ordinal());
        Assert.AreEqual("4th", 4.Ordinal());
        Assert.AreEqual("5th", 5.Ordinal());
        Assert.AreEqual("6th", 6.Ordinal());
        Assert.AreEqual("7th", 7.Ordinal());
        Assert.AreEqual("8th", 8.Ordinal());
        Assert.AreEqual("9th", 9.Ordinal());
        Assert.AreEqual("10th", 10.Ordinal());
        Assert.AreEqual("11th", 11.Ordinal());
        Assert.AreEqual("12th", 12.Ordinal());
        Assert.AreEqual("13th", 13.Ordinal());
        Assert.AreEqual("14th", 14.Ordinal());
        Assert.AreEqual("20th", 20.Ordinal());
        Assert.AreEqual("21st", 21.Ordinal());
        Assert.AreEqual("22nd", 22.Ordinal());
        Assert.AreEqual("23rd", 23.Ordinal());
        Assert.AreEqual("24th", 24.Ordinal());
        Assert.AreEqual("100th", 100.Ordinal());
        Assert.AreEqual("101st", 101.Ordinal());
        Assert.AreEqual("102nd", 102.Ordinal());
        Assert.AreEqual("103rd", 103.Ordinal());
        Assert.AreEqual("104th", 104.Ordinal());
        Assert.AreEqual("110th", 110.Ordinal());
        Assert.AreEqual("111th", 111.Ordinal());
        Assert.AreEqual("112th", 112.Ordinal());
        Assert.AreEqual("113th", 113.Ordinal());
        Assert.AreEqual("114th", 114.Ordinal());
        Assert.AreEqual("120th", 120.Ordinal());
        Assert.AreEqual("121st", 121.Ordinal());
        Assert.AreEqual("122nd", 122.Ordinal());
        Assert.AreEqual("123rd", 123.Ordinal());
        Assert.AreEqual("124th", 124.Ordinal());
    }

15

Simples, limpo, rápido

    private static string GetOrdinalSuffix(int num)
    {
        if (num.ToString().EndsWith("11")) return "th";
        if (num.ToString().EndsWith("12")) return "th";
        if (num.ToString().EndsWith("13")) return "th";
        if (num.ToString().EndsWith("1")) return "st";
        if (num.ToString().EndsWith("2")) return "nd";
        if (num.ToString().EndsWith("3")) return "rd";
        return "th";
    }

Ou melhor ainda, como método de extensão

public static class IntegerExtensions
{
    public static string DisplayWithSuffix(this int num)
    {
        if (num.ToString().EndsWith("11")) return num.ToString() + "th";
        if (num.ToString().EndsWith("12")) return num.ToString() + "th";
        if (num.ToString().EndsWith("13")) return num.ToString() + "th";
        if (num.ToString().EndsWith("1")) return num.ToString() + "st";
        if (num.ToString().EndsWith("2")) return num.ToString() + "nd";
        if (num.ToString().EndsWith("3")) return num.ToString() + "rd";
        return num.ToString() + "th";
    }
}

Agora você pode simplesmente ligar

int a = 1;
a.DisplayWithSuffix(); 

ou mesmo tão direto quanto

1.DisplayWithSuffix();

14

Você terá que fazer o seu próprio. Do alto da minha cabeça:

public static string Ordinal(this int number)
{
  var work = number.ToString();
  if ((number % 100) == 11 || (number % 100) == 12 || (number % 100) == 13)
    return work + "th";
  switch (number % 10)
  {
    case 1: work += "st"; break;
    case 2: work += "nd"; break;
    case 3: work += "rd"; break;
    default: work += "th"; break;
  }
  return work;
}

Você pode então fazer

Console.WriteLine(432.Ordinal());

Editado para 11/11/13 exceções. Eu disse do alto da minha cabeça :-)

Editado para 1011 - outros já corrigiram isso, só queremos garantir que outros não acessem esta versão incorreta.


12

Gostei bastante de elementos das soluções de Stu e samjudson e trabalhei juntos no que considero uma combinação utilizável:

    public static string Ordinal(this int number)
    {
        const string TH = "th";
        var s = number.ToString();

        number %= 100;

        if ((number >= 11) && (number <= 13))
        {
            return s + TH;
        }

        switch (number % 10)
        {
            case 1:
                return s + "st";
            case 2:
                return s + "nd";
            case 3:
                return s + "rd";
            default:
                return s + TH;
        }
    }

1
qual é a lógica por trás de usar uma constante para "th"?
nickf

porque é usado duas vezes no código. Apenas utilizando a sabedoria milenar de que você não deve se repetir :) Nesse caso, o tempo de execução do .NET deve criar apenas uma cópia da string, enquanto com dois "th" s no código, haverá duas strings criadas e referenciado na memória.
Jesse C. Slicer

25
e também, se o valor de TH mudar, você será definido.
Eclipse

7
@ Jess - Você recebe meu +1, mas eu não acredito que o .NET lida com seqüências de caracteres dessa maneira, consulte yoda.arachsys.com/csharp/strings.html#interning , minha leitura disso é cada referência ao literal "th" referenciaria o mesmo bit de memória. Mas eu concordo sobre DRY :)
si618

4
Remover uma duplicação como essa dificulta a legibilidade, daí a confusão "Por que o TH?". Não acho que o DRY deva ser interpretado como 'remova toda duplicação, qualquer que seja o custo'.
SeeNoWeevil

8

Embora eu ainda não tenha comparado isso, você poderá obter melhor desempenho evitando todas as declarações de caso condicionais.

Isso é java, mas uma porta para C # é trivial:

public class NumberUtil {
  final static String[] ORDINAL_SUFFIXES = {
    "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
  };

  public static String ordinalSuffix(int value) {
    int n = Math.abs(value);
    int lastTwoDigits = n % 100;
    int lastDigit = n % 10;
    int index = (lastTwoDigits >= 11 && lastTwoDigits <= 13) ? 0 : lastDigit;
    return ORDINAL_SUFFIXES[index];
  }

  public static String toOrdinal(int n) {
    return new StringBuffer().append(n).append(ordinalSuffix(n)).toString();
  }
}

Observe que a redução de condicionais e o uso da pesquisa de matriz devem acelerar o desempenho se gerar muitos ordinais em um loop restrito. No entanto, também concordo que isso não é tão legível quanto a solução da instrução de caso.


Lamento que eu tenha comparado isso em C #, sua versão não é mais rápida que a solução de si618.
GY_

verifique esta resposta stackoverflow.com/a/58378465/2583579 para alguns benchmarks
Dan Dohotaru

3

Semelhante à solução de Ryan, mas ainda mais básica, eu apenas uso uma matriz simples e uso o dia para procurar o ordinal correto:

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" };
DateTime D = DateTime.Now;
String date = "Today's day is: "+ D.Day.ToString() + ordinals[D.Day];

Eu não tive a necessidade, mas eu assumiria que você poderia usar uma matriz multidimensional se quisesse ter suporte a vários idiomas.

Pelo que me lembro dos meus dias na Uni, esse método requer um esforço mínimo do servidor.


2

Eu uso esta classe de extensão:

public static class Int32Extensions
{
    public static string ToOrdinal(this int i)
    {
        return (i + "th")
            .Replace("1th", "1st")
            .Replace("2th", "2nd")
            .Replace("3th", "3rd");
    }
}

11, 12 e 13
Kcoder

2

Solicitou a versão "menos redundância" da resposta de samjudson ...

public static string AddOrdinal(int number)
{
    if (number <= 0) return number.ToString();

    string GetIndicator(int num)
    {
        switch (num % 100)
        {
            case 11:
            case 12:
            case 13:
                return "th";
        }

        switch (num % 10)
        {
            case 1:
                return "st";
            case 2:
                return "nd";
            case 3:
                return "rd";
            default:
                return "th";
        }
    }

    return number + GetIndicator(number);
}

2
Eu exporia "GetIndicator" como a public statice o renomearia para um nome mais mnemônico (por exemplo, "OrdinalSuffix"). O chamador pode querer a parte numérica em diferentes formatos (por exemplo, com vírgulas).
Tom

2
        private static string GetOrd(int num) => $"{num}{(!(Range(11, 3).Any(n => n == num % 100) ^ Range(1, 3).All(n => n != num % 10)) ? new[] { "ˢᵗ", "ⁿᵈ", "ʳᵈ" }[num % 10 - 1] : "ᵗʰ")}";

Se alguém procura um forro: p


1
public static string OrdinalSuffix(int ordinal)
{
    //Because negatives won't work with modular division as expected:
    var abs = Math.Abs(ordinal); 

    var lastdigit = abs % 10; 

    return 
        //Catch 60% of cases (to infinity) in the first conditional:
        lastdigit > 3 || lastdigit == 0 || (abs % 100) - lastdigit == 10 ? "th" 
            : lastdigit == 1 ? "st" 
            : lastdigit == 2 ? "nd" 
            : "rd";
}

1

EDIT : Como YM_Industries aponta no comentário, a resposta de samjudson funciona para números acima de 1000, o comentário de nickf parece ter desaparecido e não me lembro qual foi o problema que vi. Deixou esta resposta aqui para os tempos de comparação.

Muitos deles não funcionam para números> 999, como o nickf apontou em um comentário (EDIT: agora ausente).

Aqui está uma versão com base em uma versão modificada do samjudson de resposta aceita que faz.

public static String GetOrdinal(int i)
{
    String res = "";

    if (i > 0)
    {
        int j = (i - ((i / 100) * 100));

        if ((j == 11) || (j == 12) || (j == 13))
            res = "th";
        else
        {
            int k = i % 10;

            if (k == 1)
                res = "st";
            else if (k == 2)
                res = "nd";
            else if (k == 3)
                res = "rd";
            else
                res = "th";
        }
    }

    return i.ToString() + res;
}

Também a resposta de Shahzad Qureshi usando manipulação de cordas funciona bem, no entanto, tem uma penalidade de desempenho. Para gerar muitos desses, um exemplo de programa LINQPad torna a versão da string 6 a 7 vezes mais lenta que a do inteiro (embora você precise gerar muito para perceber).

Exemplo de LINQPad:

void Main()
{
    "Examples:".Dump();

    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 })
        Stuff.GetOrdinal(i).Dump();

    String s;

    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

    for(int iter = 0; iter < 100000; iter++)
        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 })
            s = Stuff.GetOrdinal(i);

    "Integer manipulation".Dump();
    sw.Elapsed.Dump();

    sw.Restart();

    for(int iter = 0; iter < 100000; iter++)
        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 })
            s = (i.ToString() + Stuff.GetOrdinalSuffix(i));

    "String manipulation".Dump();
    sw.Elapsed.Dump();
}

public class Stuff
{
        // Use integer manipulation
        public static String GetOrdinal(int i)
        {
                String res = "";

                if (i > 0)
                {
                        int j = (i - ((i / 100) * 100));

                        if ((j == 11) || (j == 12) || (j == 13))
                                res = "th";
                        else
                        {
                                int k = i % 10;

                                if (k == 1)
                                        res = "st";
                                else if (k == 2)
                                        res = "nd";
                                else if (k == 3)
                                        res = "rd";
                                else
                                        res = "th";
                        }
                }

                return i.ToString() + res;
        }

        // Use string manipulation
        public static string GetOrdinalSuffix(int num)
        {
                if (num.ToString().EndsWith("11")) return "th";
                if (num.ToString().EndsWith("12")) return "th";
                if (num.ToString().EndsWith("13")) return "th";
                if (num.ToString().EndsWith("1")) return "st";
                if (num.ToString().EndsWith("2")) return "nd";
                if (num.ToString().EndsWith("3")) return "rd";
                return "th";
        }
}

Não consigo encontrar o comentário de @ nickf, o que há de errado com a resposta de samjudson? Parece-me que ele lida com números acima de 1000 muito bem, sendo muito mais legível que o seu.
Joshua Walsh

1
É um comentário justo, acabei de executar um conjunto de testes e não consigo encontrar nenhum problema. Parece que não houve nenhuma edição na resposta de Sam, então só posso imaginar que estava ficando louco. Eu editei minha resposta para refletir isso.
Whelkaholism

1
Haha, todos nós temos momentos assim, não é? Nós olhamos para o código antigo e dizemos "por que diabos eu escrevi isso?"
Joshua Walsh

1

Com base nas outras respostas:

public static string Ordinal(int n)
{   
    int     r = n % 100,     m = n % 10;

    return (r<4 || r>20) && (m>0 && m<4) ? n+"  stndrd".Substring(m*2,2) : n+"th";                                              
}

3
1º LUGAR: Resposta mais desnecessariamente enigmática. "Desnecessariamente": os benefícios de tamanho / desempenho do código não valem custos de legibilidade. "Críptico": tradução significativa necessária para mapear para os requisitos de "leigo".
Tom

0

FWIW, para MS-SQL, essa expressão fará o trabalho. Mantenha o primeiro WHEN ( WHEN num % 100 IN (11, 12, 13) THEN 'th') como o primeiro da lista, pois isso depende de ser tentado antes dos outros.

CASE
  WHEN num % 100 IN (11, 12, 13) THEN 'th' -- must be tried first
  WHEN num % 10 = 1 THEN 'st'
  WHEN num % 10 = 2 THEN 'nd'
  WHEN num % 10 = 3 THEN 'rd'
  ELSE 'th'
END AS Ordinal

Para Excel:

=MID("thstndrdth",MIN(9,2*RIGHT(A1)*(MOD(A1-11,100)>2)+1),2)

A expressão (MOD(A1-11,100)>2)é TRUE (1) para todos os números, exceto qualquer final em 11,12,13(FALSE = 0). Portanto, 2 * RIGHT(A1) * (MOD(A1-11,100)>2) +1)termina como 1 em 12/11/13, caso contrário:
1 avalia para 3
2 a 5,
3 a 7
outros: 9
- e os 2 caracteres necessários são selecionados "thstndrdth"a partir dessa posição.

Se você realmente deseja converter isso diretamente no SQL, isso funcionou para mim para alguns valores de teste:

DECLARE @n as int
SET @n=13
SELECT SubString(  'thstndrdth'
                 , (SELECT MIN(value) FROM
                     (SELECT 9 as value UNION
                      SELECT 1+ (2* (ABS(@n) % 10)  *  CASE WHEN ((ABS(@n)+89) % 100)>2 THEN 1 ELSE 0 END)
                     ) AS Mins
                   )
                 , 2
                )

0

Esta é a implementação darte pode ser modificada de acordo com o idioma.

String getOrdinalSuffix(int num){
    if (num.toString().endsWith("11")) return "th";
    if (num.toString().endsWith("12")) return "th";
    if (num.toString().endsWith("13")) return "th";
    if (num.toString().endsWith("1")) return "st";
    if (num.toString().endsWith("2")) return "nd";
    if (num.toString().endsWith("3")) return "rd";
    return "th";
}

0

Embora haja muitas boas respostas aqui, acho que há espaço para outra, desta vez com base na correspondência de padrões, se não for para mais nada, pelo menos para uma legibilidade discutível

    public static string Ordinals1(this int number)
    {
        switch (number)
        {
            case int p when p % 100 == 11:
            case int q when q % 100 == 12:
            case int r when r % 100 == 13:
                return $"{number}th";
            case int p when p % 10 == 1:
                return $"{number}st";
            case int p when p % 10 == 2:
                return $"{number}nd";
            case int p when p % 10 == 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

e o que torna esta solução especial? nada além de incluir algumas considerações de desempenho para várias outras soluções

francamente, duvido que o desempenho realmente importe para esse cenário em particular (quem realmente precisa dos ordinais de milhões de números), mas pelo menos apresenta algumas comparações a serem levadas em conta ...

1 milhão de itens para referência (sua milhagem pode variar com base nas especificações da máquina, é claro)

com correspondência de padrões e divisões (esta resposta)

~ 622 ms

com correspondência de padrões e seqüências de caracteres (esta resposta)

~ 1967 ms

com dois comutadores e divisões (resposta aceita)

~ 637 ms

com um switch e divisões (outra resposta)

~ 725 ms

void Main()
{
    var timer = new Stopwatch();
    var numbers = Enumerable.Range(1, 1000000).ToList();

    // 1
    timer.Reset();
    timer.Start();
    var results1 = numbers.Select(p => p.Ordinals1()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with pattern matching and divisions");

    // 2
    timer.Reset();
    timer.Start();
    var results2 = numbers.Select(p => p.Ordinals2()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with pattern matching and strings");

    // 3
    timer.Reset();
    timer.Start();
    var results3 = numbers.Select(p => p.Ordinals3()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with two switches and divisons");

    // 4
    timer.Reset();
    timer.Start();
    var results4 = numbers.Select(p => p.Ordinals4()).ToList();
    timer.Stop();
    timer.Elapsed.TotalMilliseconds.Dump("with one switche and divisons");
}

public static class Extensions
{
    public static string Ordinals1(this int number)
    {
        switch (number)
        {
            case int p when p % 100 == 11:
            case int q when q % 100 == 12:
            case int r when r % 100 == 13:
                return $"{number}th";
            case int p when p % 10 == 1:
                return $"{number}st";
            case int p when p % 10 == 2:
                return $"{number}nd";
            case int p when p % 10 == 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

    public static string Ordinals2(this int number)
    {
        var text = number.ToString();
        switch (text)
        {
            case string p when p.EndsWith("11"):
                return $"{number}th";
            case string p when p.EndsWith("12"):
                return $"{number}th";
            case string p when p.EndsWith("13"):
                return $"{number}th";
            case string p when p.EndsWith("1"):
                return $"{number}st";
            case string p when p.EndsWith("2"):
                return $"{number}nd";
            case string p when p.EndsWith("3"):
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

    public static string Ordinals3(this int number)
    {
        switch (number % 100)
        {
            case 11:
            case 12:
            case 13:
                return $"{number}th";
        }

        switch (number % 10)
        {
            case 1:
                return $"{number}st";
            case 2:
                return $"{number}nd";
            case 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }

    public static string Ordinals4(this int number)
    {
        var ones = number % 10;
        var tens = Math.Floor(number / 10f) % 10;
        if (tens == 1)
        {
            return $"{number}th";
        }

        switch (ones)
        {
            case 1:
                return $"{number}th";
            case 2:
                return $"{number}nd";
            case 3:
                return $"{number}rd";
            default:
                return $"{number}th";
        }
    }
}

0

Outro item de uma linha, mas sem comparações, apenas indexando o resultado da regex em uma matriz.

public static string GetOrdinalSuffix(int input)
{
    return new []{"th", "st", "nd", "rd"}[Convert.ToInt32("0" + Regex.Match(input.ToString(), "(?<!1)[1-3]$").Value)];
}

A versão do PowerShell pode ser reduzida ainda mais:

function ord($num) { return ('th','st','nd','rd')[[int]($num -match '(?<!1)[1-3]$') * $matches[0]] }

0

Outro 1 forro.

public static string Ordinal(this int n)
{    
 return n + (new [] {"st","nd","rd" }.ElementAtOrDefault((((n + 90) % 100 - 10) % 10 - 1)) ?? "th");
}

-2

Aqui está a classe DateTime Extension. Copie, cole e divirta-se

classe estática pública DateTimeExtensions {

    public static string ToStringWithOrdinal(this DateTime d)
    {
        var result = "";
        bool bReturn = false;            

        switch (d.Day % 100)
        {
            case 11:
            case 12:
            case 13:
                result = d.ToString("dd'th' MMMM yyyy");
                bReturn = true;
                break;
        }

        if (!bReturn)
        {
            switch (d.Day % 10)
            {
                case 1:
                    result = d.ToString("dd'st' MMMM yyyy");
                    break;
                case 2:
                    result = d.ToString("dd'nd' MMMM yyyy");
                    break;
                case 3:
                    result = d.ToString("dd'rd' MMMM yyyy");
                    break;
                default:
                    result = d.ToString("dd'th' MMMM yyyy");
                    break;
            }

        }

        if (result.StartsWith("0")) result = result.Substring(1);
        return result;
    }
}

Resultado:

9 de outubro de 2014


Você está duplicando: a) a string de formato de data (X5) eb) todo o restante do método (quando surgir o provável caso de uso (se ainda não o tiver)) de que um sufixo ordinal é necessário para o dia não do mês finalidades ou mesmo um dia do mês com uma string de formato de data diferente). Use o método "OrdinalSuffix" que sugeri ser exposto na resposta de Ian Warburton em 6 de abril de 17 às 16:32 ( stackoverflow.com/questions/20156/… ).
Tom

-3

Outra alternativa que usei com base em todas as outras sugestões, mas não requer carcaça especial:

    public static string DateSuffix(int day)
    {
        if (day == 11 | day == 12 | day == 13) return "th";
        Math.DivRem(day, 10, out day);
        switch (day)
        {
            case 1:
                return "st";
            case 2:
                return "nd";
            case 3:
                return "rd";
            default:
                return "th";
        }
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.