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

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

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

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

Ответ 1

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

 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

Ответ 6

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

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

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

Ответ 7

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

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

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

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

двадцать третий

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

Я рекомендую вам написать unit тест тоже.

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

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

}

Ответ 11

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

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

Ответ 12

Вот подход, который обновляет шаблон DateTimeFormatter с правильным суффиксом, если он находит шаблон d'00', например, для дня месяца 1 он будет заменен на d'st'. После того, как шаблон был обновлен, он может быть просто передан в DateTimeFormatter, чтобы сделать все остальное.

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

Требуется, чтобы исходный шаблон обновлялся непосредственно перед каждым вызовом форматирования, например

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

Так что это полезно, если шаблон форматирования определен вне Java-кода, например, шаблона, где, как если бы вы могли определить шаблон в Java, тогда ответ @OleV.V. может быть более подходящим

Ответ 13

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

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

Ответ 14

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

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

Ответ 15

Я написал себе вспомогательный метод, чтобы получить шаблоны для этого.

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

и тогда мы можем назвать это как

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

выход

December 12th, 2018

Ответ 16

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

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

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

OR to spell the number add SP to the format

DD + SPTH = DDSPTH   result >>> FOURTH

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

Ответ 17

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

Ответ 18

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