Existe-t-il un moyen simple de créer des ordinaux en C #?


202

Existe-t-il un moyen facile en C # de créer des ordinaux pour un nombre? Par exemple:

  • 1 renvoie le 1er
  • 2 renvoie le 2e
  • 3 renvoie 3e
  • ...etc

Est-ce que cela peut être fait par le biais String.Format()ou existe-t-il des fonctions disponibles pour le faire?

Réponses:


311

Cette page vous donne une liste complète de toutes les règles de formatage numérique personnalisées:

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

Comme vous pouvez le voir, il n'y a rien là-dedans concernant les ordinaux, donc cela ne peut pas être fait en utilisant String.Format. Cependant, ce n'est pas vraiment difficile d'écrire une fonction pour le faire.

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

Mise à jour: Techniquement, les ordinaux n'existent pas pour <= 0, j'ai donc mis à jour le code ci-dessus. Supprimé également les ToString()méthodes redondantes .

Notez également que ce n'est pas internationalisé. Je n'ai aucune idée de ce à quoi ressemblent les ordinaux dans d'autres langues.


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

2
Utiliser une méthode d'extention (ou son nom - voir la réponse de @ Stu) fonctionnerait très bien ici. @Si, l'ajout de cette condition serait très facile si nécessaire.
strager

12
Vous avez oublié '11e, 12e 13e' ... devrait être une question d'entrevue. :-)
Holf

2
Ouais, les programmeurs sont bizarres;)
samjudson

2
@IanWarburton Il n'y a pas de redondance car une seule déclaration de retour sera frappée. Si vous n'êtes pas satisfait de la réponse, veuillez fournir la vôtre, en nous montrant la manière "appropriée" de le faire et pourquoi elle est importante.
B2K

73

N'oubliez pas l'internationalisation!

Les solutions ici ne fonctionnent que pour l'anglais. Les choses deviennent beaucoup plus complexes si vous devez prendre en charge d'autres langues.

Par exemple, en espagnol, "1st" serait écrit comme "1.o", "1.a", "1.os" ou "1.as" selon que la chose que vous comptez est masculine, féminine ou plurielle. !

Donc, si votre logiciel doit prendre en charge différentes langues, essayez d'éviter les ordinaux.


7
@ Andomar: "Les 2 premiers lecteurs" => en italien (et espagnol aussi, je suppose) "premier" est ici pluriel. Vous avez donc le masculin singulier, le féminin singulier, le masculin pluriel, le féminin pluriel; peut-être qu'une langue a aussi un cas neutre (distinguant les choses des hommes / animaux)
M.Turrini

2
Cela dit, vous n'avez pas à éviter les ordinaux: incluez-les dans la localisation, une fois que vous connaissez tous les cas auxquels vous pourriez être confronté, ou (faites en sorte que votre client) accepte certaines limitations.
M.Turrini

26
Cela explique pourquoi l'équipe .NET a évité de l'ajouter aux formateurs DateTime
Chris S

moment.js a une fonction de mise en forme "ordinale" par locale, donc cela semble faisable, souhaite également qu'ils l'aient fait dans .NET pour DateTime
Guillaume86

5
Ce serait très simple si vous utilisiez tous le "." caractère pour les ordinaux, comme nous le faisons en allemand)))) 1. 2. 3. 4. 5., etc. cas grammaticaux avec 3 articles différents, en plus des cas singuliers et pluriels des 12 combinaisons différentes. À bien y penser, les Russes n'en ont pas 2 de plus, plus le vocativ, et certaines langues nordiques en ont 15, je pense. J'aurais adoré voir cette implémentation dans .NET.
Stefan Steiger

22

Ma version de la version de Jesse des versions de Stu et samjudson :)

Test unitaire inclus pour montrer que la réponse acceptée est incorrecte lorsque le nombre <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

Simple, propre, rapide

    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 mieux encore, comme méthode d'extension

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

Maintenant, vous pouvez simplement appeler

int a = 1;
a.DisplayWithSuffix(); 

ou même aussi direct que

1.DisplayWithSuffix();

14

Vous devrez lancer le vôtre. Du haut de ma tête:

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

Vous pouvez alors faire

Console.WriteLine(432.Ordinal());

Modifié pour les exceptions du 11/12/13. J'ai dit du haut de ma tête :-)

Modifié pour 1011 - d'autres ont déjà corrigé cela, je veux juste m'assurer que les autres ne saisissent pas cette version incorrecte.


12

J'ai plutôt aimé les éléments des solutions de Stu et de samjudson et les ai travaillés ensemble dans ce que je pense être un combo utilisable:

    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
quelle est la raison derrière l'utilisation d'une constante pour "th"?
nickf

car il est utilisé deux fois dans le code. En utilisant simplement la sagesse séculaire que vous ne devriez pas vous répéter :) Dans ce cas, le runtime .NET ne devrait créer qu'une copie de la chaîne tandis qu'avec deux "th" dans le code, il y aurait deux chaînes créées et référencé en mémoire.
Jesse C. Slicer,

25
et aussi, si la valeur de TH change, vous serez défini.
Eclipse

7
@Jesse - Vous obtenez mon +1, mais je ne crois pas que .NET gère les chaînes de cette façon, voir yoda.arachsys.com/csharp/strings.html#interning , ma lecture de cela est chaque référence au "e" littéral ferait référence au même bit de mémoire. Mais je suis d'accord sur DRY :)
si618

4
Supprimer la duplication comme celle-ci ne fait que gêner la lisibilité, je pense, d'où la confusion "Pourquoi le TH?". Je ne pense pas que DRY devrait être interprété comme «supprimez toute duplication quel qu'en soit le coût».
SeeNoWeevil

8

Bien que je n'ai pas encore évalué cela, vous devriez pouvoir obtenir de meilleures performances en évitant toutes les déclarations de cas conditionnelles.

C'est java, mais un port vers C # est 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();
  }
}

Notez que la réduction des conditions et l'utilisation de la recherche de tableau devraient accélérer les performances si vous générez beaucoup d'ordinaux dans une boucle serrée. Cependant, je concède également que ce n'est pas aussi lisible que la solution de déclaration de cas.


Désolé d'avoir comparé cela en C #, votre version n'est pas plus rapide que la solution de si618.
GY_

vérifier cette réponse stackoverflow.com/a/58378465/2583579 pour certains repères
Dan Dohotaru

3

Semblable à la solution de Ryan, mais encore plus basique, j'utilise simplement un tableau simple et utilise le jour pour rechercher l'ordinal correct:

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

Je n'en ai pas eu besoin, mais je suppose que vous pouvez utiliser un tableau multidimensionnel si vous souhaitez avoir une prise en charge multilingue.

D'après ce dont je me souviens de mes jours Uni, cette méthode nécessite un minimum d'effort de la part du serveur.


2

J'utilise cette classe d'extension:

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, 13
Kcoder

2

Version "moins redondante" de la réponse de samjudson demandée ...

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
Je voudrais exposer "GetIndicator" en tant que public staticet le renommer en un nom plus mnémonique (c'est-à-dire "OrdinalSuffix"). L'appelant peut vouloir la partie numérique dans différents formats (c'est-à-dire avec des virgules).
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] : "ᵗʰ")}";

Si vous cherchez un liner: 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 : Comme YM_Industries le souligne dans le commentaire, la réponse de samjudson fonctionne pour les nombres supérieurs à 1000, le commentaire de nickf semble avoir disparu, et je ne me souviens pas quel était le problème que j'ai vu. Laissé cette réponse ici pour les temps de comparaison.

Beaucoup d'entre eux ne fonctionnent pas pour des nombres> 999, comme l' a souligné nickf dans un commentaire (EDIT: maintenant manquant).

Voici une version basée sur une version modifiée de la réponse acceptée samjudson qui le fait.

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

Aussi la réponse de Shahzad Qureshi en utilisant la manipulation de chaînes fonctionne très bien, mais il a une pénalité de performance. Pour générer beaucoup de ceux-ci, un exemple de programme LINQPad rend la version de chaîne 6-7 fois plus lente que celle entière (bien que vous deviez en générer beaucoup pour le remarquer).

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

Je ne trouve pas le commentaire de @ nickf, quel est le problème avec la réponse de samjudson? Il me semble qu'il gère très bien les nombres supérieurs à 1000 tout en étant beaucoup plus lisible que le vôtre.
Joshua Walsh

1
C'est un commentaire juste, je viens de lancer un test et je ne trouve aucun problème. Il ne semble pas non plus avoir été modifié dans la réponse de Sam, donc je peux seulement imaginer que je devenais fou. J'ai modifié ma réponse pour refléter cela.
Whelkaholism

1
Haha, nous avons tous des moments comme ça, n'est-ce pas? Nous regardons en arrière l'ancien code et disons "pourquoi diable ai-je écrit ceci?"
Joshua Walsh

1

Sur la base des autres réponses:

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
1ER LIEU: Réponse la plus inutilement cryptée. "Inutile": les avantages de taille / performances du code ne valent pas les coûts de lisibilité. "Cryptic": une traduction importante est nécessaire pour correspondre aux exigences "Layperson".
Tom

0

FWIW, pour MS-SQL, cette expression fera le travail. Conservez le premier WHEN ( WHEN num % 100 IN (11, 12, 13) THEN 'th') en tant que premier de la liste, car cela dépend de la tentative avant les autres.

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

Pour Excel:

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

L'expression (MOD(A1-11,100)>2)est VRAIE (1) pour tous les nombres sauf toute fin en 11,12,13(FAUX = 0). Se 2 * RIGHT(A1) * (MOD(A1-11,100)>2) +1)termine donc par 1 pour le 11/12/13, sinon:
1 correspond à 3
2 à 5,
3 à 7
autres: 9
- et les 2 caractères requis sont sélectionnés parmi"thstndrdth" partir de cette position.

Si vous voulez vraiment convertir cela assez directement en SQL, cela a fonctionné pour moi pour une poignée de valeurs de test:

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

Il s'agit de l'implémentation dans dartet peut être modifié selon la langue.

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

Bien qu'il y ait beaucoup de bonnes réponses ici, je suppose qu'il y a de la place pour une autre, cette fois basée sur la correspondance des modèles, sinon pour autre chose, du moins pour une lisibilité discutable

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

et qu'est-ce qui rend cette solution spéciale? rien que le fait que j'ajoute des considérations de performances pour diverses autres solutions

franchement, je doute que les performances comptent vraiment pour ce scénario particulier (qui a vraiment besoin des ordinaux de millions de nombres) mais au moins cela fait ressortir quelques comparaisons à prendre en compte ...

1 million d'articles pour référence (votre millage peut varier en fonction des spécifications de la machine bien sûr)

avec correspondance de motifs et divisions (cette réponse)

~ 622 ms

avec correspondance de motifs et chaînes (cette réponse)

~ 1967 ms

avec deux interrupteurs et divisions (réponse acceptée)

~ 637 ms

avec un interrupteur et des divisions (une autre réponse)

~ 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

Un autre one-liner, mais sans comparaisons en indexant uniquement le résultat d'expression régulière dans un tableau.

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

La version PowerShell peut être encore raccourcie:

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

0

Encore 1 doublure.

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

-2

Voici la classe d'extension DateTime. Copiez, collez et profitez

classe statique publique 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;
    }
}

Résultat :

9 octobre 2014


Vous dupliquez: a) la chaîne de format de date (X5) et b) le reste de la méthode (lorsque le cas d'utilisation probable survient (s'il ne l'a pas déjà fait) qu'un suffixe ordinal est nécessaire pour les non-jours du mois fins ou même un jour du mois avec une chaîne de format de date différente). Utilisez la méthode "OrdinalSuffix" que j'ai suggérée d'être exposée dans la réponse d'Ian Warburton du 6 avril 17 à 16:32 ( stackoverflow.com/questions/20156/… ).
Tom

-3

Une autre alternative que j'ai utilisée sur la base de toutes les autres suggestions, mais ne nécessite aucun boîtier spécial:

    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";
        }
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.