Как вы форматируете день месяца, чтобы сказать "11th", "21st" или "23rd" (порядковый индикатор)?

Я знаю, что это даст мне день месяца как число (11, 21, 23):

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

но как вы форматируете день месяца, чтобы включить порядковый показатель, сказал 11th, 21st или 23rd?

16 ответов


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

таблица от @kaliatech хороша, но так как та же информация повторяется, она открывает шанс для ошибки. Такая ошибка фактически существует в таблице для 7tn, 17tn и 27tn (эта ошибка может быть исправлена с течением времени из-за текучей природы StackOverflow, поэтому проверьте история версий на ответ чтобы увидеть ошибки).


в JDK нет ничего, чтобы сделать это.

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

или с помощью календаря:

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

в комментариях @thorbjørn-ravn-andersen, такая таблица может быть полезна при локализации:

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

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

вопрос немного старая. Поскольку этот вопрос очень шумный, поэтому публикуя то, что я сделал, решил статическим методом в качестве util. Просто скопируйте, вставьте и используйте его!

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

для тестирования purose

пример: вызов его из основного метода!

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

выход:

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

Если вы пытаетесь быть в курсе i18n, решение становится еще более сложным.

проблема в том, что в других языках суффикс может зависеть не только от самого числа, но и от существительного, которое он считает. Например, в русском языке это будет "2-ой день", но "2-ая неделя" (это означает "2-й день", но "2-я неделя"). Это не применимо, если мы форматируем только дни, но в более общем случае вы должны знать о сложности.

Я думаю, хорошее решение (у меня не было времени фактически реализовать) было бы расширить SimpleDateFormetter для применения локального MessageFormat перед передачей родительскому классу. Таким образом, Вы сможете поддержать, скажем, для форматов марта %M, чтобы получить "3-й", %MM, чтобы получить "03-й" и %MMM, чтобы получить "третий". Снаружи этот класс выглядит как обычный SimpleDateFormatter, но поддерживает больше форматов. Также, если этот шаблон будет по ошибке применен обычным SimpleDateFormetter, результат будет неправильно отформатирован, но все же читаемый.


многие примеры здесь не будут работать для 11, 12, 13. Это является более общим и будет работать для всех случаев.

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

Я не могу быть удовлетворен ответами, призывающими к решению только на английском языке, основанному на ручных форматах. Я искал правильное решение некоторое время, и я, наконец, нашел его.

вы должны использовать RuleBasedNumberFormat. Он работает отлично и уважительно относится к местности.


существует более простой и надежный способ сделать это. Функция, которую вам нужно будет использовать, - getDateFromDateString (dateString); она в основном удаляет st/nd/rd/th из строки даты и просто анализирует ее. Вы можете изменить свой SimpleDateFormat на что угодно, и это будет работать.

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;

}


единственная проблема с решением, предоставленным Грегом, заключается в том, что он не учитывает число больше 100 с окончанием номеров "teen". Например, 111 должен быть 111-м, а не 111-м. Вот мое решение:--2-->

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

Я хотел бы внести свой вклад в современное ответа. The SimpleDateFormat класс был в порядке, когда вопрос был задан 8 лет назад, но вы должны избегать его сейчас, поскольку он не только давно устарел, но и, как известно, хлопотно. Использовать .

редактировать

DateTimeFormatterBuilder.appendText(TemporalField, Map<Long, String>) отлично подходит для этой цели. Используя его, мы создаем форматер, который делает работу за нас:

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

вывод из этого фрагмента есть:

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

ответ

этот код короче, но ИМХО не такой элегантный.

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

запуск этого фрагмента только что я получил

23-го

одна из многих особенностей java.time это то, что это просто и надежно, чтобы получить день месяца в качестве int, который, очевидно, необходим для выбора правильного суффикса из таблицы.

рекомендую Вы тоже пишете модульный тест.

PS я не пробовал, но я ожидал бы, что аналогичный форматтер также может быть использован для извлечение строка даты, содержащая порядковые номера, такие как 1st, 2nd, etc.

ссылки: Oracle учебник: дата и время объясняя, как использовать java.time.


в Котлине вы можете использовать вот так

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

набор такой

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

Если вам это нужно на Android вы можете проверить ответ

Это интернационализированное решение. И вам не нужно изобретать велосипед;)


ниже приведен более эффективный ответ на вопрос, а не жесткого кодирования стиль.

чтобы изменить день на порядковый номер, вам нужно использовать следующий суффикс.

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

OR to spell the number add SP to the format

DD + SPTH = DDSPTH   result >>> FOURTH

найдите мой завершенный ответ в этой вопрос.


следующий метод может быть использован для получения форматированной строки даты, которая передается в него. Он отформатирует дату, чтобы сказать 1,2, 3, 4 .. использование SimpleDateFormat в Java. например:- 1 сентября 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);
        }

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