Pergunta

Eu sei que isso me dará o dia do mês como um número (11, 21, 23):

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

Mas como você formate o dia do mês para incluir um Indicador ordinal, dizer 11th, 21st ou 23rd?

Foi útil?

Solução

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

A tabela de @kaliatech é boa, mas como as mesmas informações são repetidas, abre a chance de um bug. Esse bug realmente existe na tabela para 7tn, 17tn, e 27tn (Este bug pode ser corrigido com o passar do tempo devido à natureza fluida do StackOverflow, então verifique o histórico da versão na resposta para ver o erro).

Outras dicas

Não há nada no JDK para fazer isso.

  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 usando calendário:

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

Por comentários de @thorbjørn-ravn-Andersen, uma mesa como essa pode ser útil ao localizar:

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

A pergunta é pouco velha. Como essa pergunta é muito barulhenta, postar o que eu resolvi com o método estático como UTIL. Basta copiar, colar e usá -lo!

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

Para testar o Purose

Exemplo: chamando -o do método principal!

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

Resultado:

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

Eu gostaria de contribuir com a resposta moderna. o SimpleDateFormat A aula estava bem para usar quando a pergunta foi feita há 8 anos, mas você deve evitá -la agora, pois não está apenas desatualizada por muito tempo, mas também notoriamente problemática. Usar java.time em vez de.

Editar

DateTimeFormatterBuilder.appendText(TemporalField, Map<Long, String>) é ótimo para esse fim. Usando, construímos um formatador que faz o trabalho para nós:

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

A saída deste snippet é:

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

Resposta antiga

Este código é mais curto, mas não é tão elegante.

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

Executando este trecho agora eu tenho

23º

Uma das muitas características de java.time é isso direto e confiável para obter o dia do mês como um int, o que é obviamente necessário para escolher o sufixo certo da mesa.

Eu recomendo que você escreva um teste de unidade também.

Ps Um formatador semelhante também pode ser usado para análise uma string de data contendo números ordinais como 1st, 2nd, etc. Isso foi feito em essa questão: Java - Data de análise com segundos opcionais.

Link: Oracle Tutorial: Data Hora explicando como usar java.time.

Se você tentar estar ciente de I18n, a solução ficará ainda mais complicada.

O problema é que, em outros idiomas, o sufixo pode depender não apenas do número em si, mas também do substantivo que conta. Por exemplo, em russo, seria "2-й й н н", mas "2 a аеiлл" (isso significa "2º dia", mas "2ª semana"). Isso não é aplicado se formatamos apenas dias, mas, em um caso um pouco mais genérico, você deve estar ciente da complexidade.

Eu acho que uma boa solução (não tive tempo de implementar) seria estender o SimpleDateFormEtter para aplicar a MessageFormat com reconhecimento de localidade antes de passar para a classe pai. Dessa forma, você seria capaz de apoiar, deixe dizer nos formatos de março %m para obter "3-RD", %mm para obter "03-rd" e %mmm para obter "terceiro". De fora desta aula, parece um SimpleDateFormatter regular, mas suporta mais formatos. Além disso, se esse padrão fosse por engano aplicado por simpledEformetter regular, o resultado seria formatado incorretamente, mas ainda legível.

Muitos dos exemplos aqui não funcionarão para 11, 12, 13. Isso é mais genérico e funcionará para todos os casos.

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

Não posso ficar satisfeito com as respostas que pedem uma solução somente em inglês com base em formatos manuais. Estou procurando uma solução adequada há um tempo e finalmente a encontrei.

Você deveria estar usando REGBASEDNUMBERFORMAT. Funciona perfeitamente e respeita o local.

Existe uma maneira mais simples e segura de fazer isso. A função que você precisará usar é getDateFromDateString (DATEstring); Ele basicamente remove a string sT/nd/rd/th de uma data e simplesmente a analisa. Você pode alterar seu SimpleDateFormat para qualquer coisa e isso funcionará.

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;

}

O único problema com a solução fornecida por Greg é que ela não representa o número maior que 100 com os números "adolescentes" terminando. Por exemplo, 111 devem ser 111, não 111º. Esta é a minha solução:

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

Aqui está uma abordagem que atualiza um padrão DateTimeFormatter com o sufixo correto literal se encontrar o padrão d'00', por exemplo, para o dia do mês 1, seria substituído por d'st'. Depois que o padrão for atualizado, ele pode ser alimentado apenas no DateTimeFormatter para fazer o resto.

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

Exige que o padrão original seja atualizado imediatamente antes de cada chamada de formatação, por exemplo

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

Portanto, isso é útil se o padrão de formatação for definido fora do código Java, por exemplo, um modelo, onde se você pudesse definir o padrão em Java, então a resposta por @olev.v. Pode ser mais apropriado

Eu me escrevi um método auxiliar para obter padrões para isso.

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

E então podemos chamá -lo como

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

a saída é

December 12th, 2018

Em Kotlin, você pode usar assim

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

definido assim

  txt_chat_time_me.text = changeDateFormats("SERVER_DATE", "DATE")

Se você precisar disso no Android, você pode conferir esta resposta

É uma solução internacionalizada, no entanto. E você não precisa reinventar a bicicleta;)

O método a seguir pode ser usado para obter a sequência formatada da data que é passada para ele. Formatará a data para dizer 1º, 2º, 3º, 4º .. Usando o SimpleDateFormat em Java. Por exemplo:- 1º de setembro de 2015

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

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

A seguir, é apresentada uma resposta mais eficiente para a pergunta, em vez de codificar o estilo.

Para mudar o dia para o número ordinal, você precisa usar o seguinte sufixo.

DD +     TH = DDTH  result >>>> 4TH

OR to spell the number add SP to the format

DD + SPTH = DDSPTH   result >>> FOURTH

Encontre minha resposta completa em isto pergunta.

public String getDaySuffix(int inDay)
{
  String s = String.valueOf(inDay);

  if (s.endsWith("1"))
  {
    return "st";
  }
  else if (s.endsWith("2"))
  {
    return "nd";
  }
  else if (s.endsWith("3"))
  {
    return "rd";
  }
  else
  {
    return "th";
  }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top