Как вы форматируете день месяца, чтобы сказать «11-й», «21-й» или «23-й» (порядковый индикатор)? - PullRequest
126 голосов
/ 25 октября 2010

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

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

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

Ответы [ 18 ]

156 голосов
/ 25 октября 2010
// 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, поэтому проверьте историю версий в ответе чтобы увидеть ошибку).

47 голосов
/ 25 октября 2010

В 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" };
32 голосов
/ 12 декабря 2011
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";
    }
}
14 голосов
/ 05 ноября 2015

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

 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
14 голосов
/ 25 октября 2010
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)];
}
8 голосов
/ 26 сентября 2014

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

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

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

7 голосов
/ 16 мая 2018

Я хотел бы внести современный ответ.Класс SimpleDateFormat можно было использовать, когда вопрос был задан 8 лет назад, но вам следует избегать его сейчас, поскольку он не только давно устарел, но и общеизвестно проблематичен.Вместо этого используйте java.time.

Edit

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

Запустив этот фрагмент только сейчас, я получил

23rd

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

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

PS Подобный форматер также можно использовать для синтаксического анализа строки даты, содержащей порядковые номера, такие как 1st, 2nd и т. д. Это было сделано в этот вопрос: Java - Дата разбора с дополнительными секундами .

Ссылка: Учебное пособие по Oracle: Дата и время , объясняющие, как использовать java.time.

4 голосов
/ 15 февраля 2015

Многие из приведенных здесь примеров не будут работать для 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";
}
3 голосов
/ 18 октября 2017

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

Вы должны использовать RuleBasedNumberFormat . Это работает отлично, и это с уважением к Locale.

2 голосов
/ 08 августа 2011

Существует более простой и надежный способ сделать это. Вам нужно использовать функцию 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;

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...