web-dev-qa-db-fra.com

Comment formater le jour du mois pour dire "11", "21" ou "23" (indicateur ordinal)?

Je sais que cela me donnera le jour du mois sous forme de nombre (11, 21, 23):

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

Mais comment formater le jour du mois pour inclure un indicateur ordinal , disons 11th, 21st ou 23rd?

124
Ken Hume
// https://github.com/google/guava
import static com.google.common.base.Preconditions.*;

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

Le tableau de @kaliatech est sympa, mais comme la même information est répétée, cela ouvre la possibilité d’un bogue. Un tel bogue existe réellement dans la table pour 7tn, 17tn et 27tn (ce bogue pourrait être corrigé avec le temps en raison de la nature fluide de StackOverflow, consultez donc l'historique des versions sur la réponse pour voir l'erreur) .

154
Greg Mattes

JDK n'a rien à faire.

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

Ou en utilisant le calendrier:

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

Selon les commentaires de @ thorbjørn-ravn-andersen, un tableau comme celui-ci peut être utile pour localiser:

  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" };
47
kaliatech
private String getCurrentDateInSpecificFormat(Calendar currentCalDate) {
    String dayNumberSuffix = getDayNumberSuffix(currentCalDate.get(Calendar.DAY_OF_MONTH));
    DateFormat dateFormat = new SimpleDateFormat(" d'" + dayNumberSuffix + "' MMMM yyyy");
    return dateFormat.format(currentCalDate.getTime());
}

private String getDayNumberSuffix(int day) {
    if (day >= 11 && day <= 13) {
        return "th";
    }
    switch (day % 10) {
    case 1:
        return "st";
    case 2:
        return "nd";
    case 3:
        return "rd";
    default:
        return "th";
    }
}
32
Anand Shekhar
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)];
}
14
ocodo

La question est un peu vieille. Comme cette question est très bruyante, je publie ce que j’ai résolu avec la méthode statique comme util. Il suffit de copier, coller et l'utiliser!

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

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

Pour tester purose

Exemple: l'appel de la méthode principale!

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

Sortie:

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
13
Sarz

Si vous essayez d’être conscient de l’i18n, la solution devient encore plus compliquée.

Le problème est que dans d’autres langues, le suffixe peut dépendre non seulement du nombre lui-même, mais également du nom qu’il compte. Par exemple, en russe, ce serait "2-ой день", mais "2-ая неделя" (cela signifie "2ème jour", mais "2ème semaine"). Cela ne s'applique pas si nous ne formatons que quelques jours, mais dans un cas un peu plus générique, vous devez être conscient de la complexité.

Je pense que la solution de Nice (je n'ai pas eu le temps de l'implémenter) serait d'étendre SimpleDateFormetter pour appliquer MessageFormat compatible Locale avant de passer à la classe parente. De cette façon, vous seriez en mesure de supporter, disons, pour les formats de mars,% M, d'obtenir le "3ème",% MM, le "03ème" et% MMM, le "troisième". De l’extérieur de cette classe, elle ressemble à SimpleDateFormatter, mais prend en charge plusieurs formats. De plus, si ce modèle était appliqué par erreur par SimpleDateFormetter normal, le résultat serait mal formaté, mais lisible.

7
C.A.B.

Je voudrais apporter la réponse moderne. La classe SimpleDateFormat était correcte à utiliser lorsque la question a été posée il y a 8 ans, mais vous devriez l'éviter maintenant, car elle est non seulement obsolète depuis longtemps, mais également gênante. Utilisez Java.time à la place.

Modifier

DateTimeFormatterBuilder.appendText(TemporalField, Map<Long, String>) est idéal pour cela. En l'utilisant, nous construisons un formateur qui fait le travail pour nous:

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

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

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

La sortie de cet extrait est:

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

Ancienne réponse

Ce code est plus court, mais à mon humble avis pas si élégant.

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

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

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

Lancer cet extrait tout à l'heure, je me suis

23ème

L’une des nombreuses caractéristiques de Java.time est qu’il est simple et fiable d’obtenir le jour du mois sous la forme int, ce qui est évidemment nécessaire pour choisir le suffixe approprié dans le tableau.

Je vous recommande d'écrire un test unitaire aussi.

PS Un formateur similaire peut également être utilisé pour analyse une chaîne de date contenant des nombres ordinaux tels que 1st, 2nd, etc. Cette opération a été effectuée dans cette question: Java - Analyse date avec secondes optionnelles .

Link:Tutoriel Oracle: Date Time expliquant comment utiliser Java.time.

5
Ole V.V.

Je ne peux pas me satisfaire des réponses appelant une solution en anglais uniquement basée sur des formats manuels. Je cherche une solution appropriée depuis un moment et je l’ai enfin trouvée.

Vous devriez utiliser RuleBasedNumberFormat . Cela fonctionne parfaitement et est respectueux de la région. 

3

Beaucoup d'exemples ici ne fonctionneront pas pour 11, 12, 13. Ceci est plus générique et fonctionnera dans tous les cas.

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

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

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

                default:
                    return "" + date + "th";
}
3
sivag1

Il existe un moyen simple et sûr de procéder. La fonction que vous devez utiliser est getDateFromDateString (dateString); Il supprime fondamentalement les st/nd/rd/th d'une chaîne de date et les analyse simplement. Vous pouvez changer votre SimpleDateFormat en n'importe quoi et cela fonctionnera.

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

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

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

}

2
Erdem Memisyazici

Le seul problème avec la solution fournie par Greg est qu’elle ne prend pas en compte les nombres supérieurs à 100 avec les numéros "adolescents" se terminant. Par exemple, 111 devrait être 111ème, pas 111ème. Ceci est ma solution:

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

    if ( hunRem - tenRem == 10 )
    {
        return "th";
    }
    switch ( tenRem )
    {
    case 1:
        return "st";
    case 2:
        return "nd";
    case 3:
        return "rd";
    default:
        return "th";
    }
}
2
Talisphere

Voici une approche qui met à jour un modèle DateTimeFormatter avec le littéral suffixe correct s'il trouve le modèle d'00', par exemple. pour le jour du mois 1, il serait remplacé par d'st'. Une fois que le modèle a été mis à jour, vous pouvez simplement l'introduire dans le DateTimeFormatter pour faire le reste. 

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

Le modèle original doit être mis à jour juste avant chaque appel de formatage, par exemple.

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

Cela est donc utile si le modèle de formatage est défini en dehors du code Java, par exemple. un modèle où, comme si vous pouviez définir le modèle en Java, puis la réponse de @ OleV.V. pourrait être plus approprié

1
tazmaniax

J'ai écrit moi-même une méthode d'assistance pour obtenir des modèles pour cela.

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

et alors nous pouvons l'appeler comme

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

la sortie est 

December 12th, 2018
1
SamzSakerz

Si vous en avez besoin sur Android, vous pouvez consulter cette réponse

C'est une solution internationalisée, cependant. Et vous n'avez pas besoin de réinventer le vélo;)

0
Alexander Krol

En kotlin, vous pouvez utiliser comme ceci

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

            var date: Date? = null

            date = formatterOld.parse(dateString)

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

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

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

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

        return result
    }


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

mettre comme ça

  txt_chat_time_me.text = changeDateFormats("SERVER_DATE", "DATE")
0
Mohit Suthar