170

我知道这会给我一个月中的日期作为数字(11,,,2123

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

但是你如何格式化一个月中的一天以包含一个序数指示符,比如说11th21st23rd

4

23 回答 23

204
// 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 的表格很不错,但由于重复了相同的信息,它打开了一个错误的机会。这样的错误实际上存在于 、 和 的表中7tn17tn由于27tnStackOverflow 的流动性,这个错误可能会随着时间的推移而得到修复,因此请检查答案上的版本历史以查看错误)。

于 2010-10-25T00:48:10.227 回答
57

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" };
于 2010-10-25T00:09:15.453 回答
45
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";
    }
}
于 2011-12-12T12:40:45.637 回答
25

我想贡献现代答案。SimpleDateFormat8 年前提出这个问题时,该课程还可以使用,但现在您应该避免使用它,因为它不仅已经过时,而且出了名的麻烦。改为使用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()]);

刚刚运行这个片段我得到了

23日

的众多特性之一java.time是,将月份中的日期作为 获取是简单而可靠的int,这显然是从表中选择正确后缀所必需的。

我建议你也写一个单元测试。

PS类似的格式化程序也可用于解析包含诸如1st,2nd等序数的日期字符串。这是在这个问题中完成的:Java - Parse date with optional seconds

链接: Oracle 教程:日期时间解释如何使用java.time.

于 2018-05-16T11:32:11.533 回答
19

问题有点老了。由于这个问题非常嘈杂,所以将我用静态方法解决的问题作为实用程序发布。只需复制、粘贴和使用它!

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

用于测试目的

示例:从 main 方法调用它!

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
于 2015-11-05T09:22:46.863 回答
18
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)];
}
于 2010-10-25T01:26:52.427 回答
9

如果您尝试了解 i18n,则解决方案会变得更加复杂。

问题在于,在其他语言中,后缀可能不仅取决于数字本身,还取决于它所计数的名词。例如,在俄语中,它会是“2-ой день”,而是“2-ая неделя”(这些意思是“第 2 天”,但“第 2 周”)。如果我们只格式化几天,这不适用,但在更一般的情况下,您应该注意复杂性。

我认为很好的解决方案(我没有时间实际实现)是扩展 SimpleDateFormetter 以在传递给父类之前应用 Locale-aware MessageFormat。通过这种方式,您将能够支持假设 March 格式 %M 获得“3-rd”,%MM 获得“03-rd”和 %MMM 获得“third”。从外部看,这个类看起来像普通的 SimpleDateFormatter,但支持更多格式。此外,如果常规 SimpleDateFormetter 错误地应用了此模式,则结果的格式将不正确,但仍然可读。

于 2014-09-26T17:12:34.600 回答
7

此处的许多示例不适用于 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";
}
于 2015-02-15T16:29:24.860 回答
5

对于要求基于手动格式的纯英语解决方案的答案,我无法满足。我一直在寻找合适的解决方案一段时间,我终于找到了。

您应该使用RuleBasedNumberFormat。它运行良好,并且尊重语言环境。

于 2017-10-18T14:47:13.203 回答
5

RuleBasedNumberFormat在 ICU 图书馆

我很欣赏@Pierre-Olivier Dybman ( http://www.icu-project.org/apiref/icu4j/com/ibm/icu/text/RuleBasedNumberFormat.html ) 提供的 ICU 项目库的链接,但仍然需要弄清楚了解如何使用它,所以RuleBasedNumberFormat下面是一个使用示例。

它只会格式化单个数字而不是整个日期,因此如果以以下格式查找日期,则需要构建一个组合字符串:例如,2 月 3 日星期一。

下面的代码将 设置RuleBasedNumberFormat为给定语言环境的序数格式,创建一个java.time ZonedDateTime,然后将带有序数的数字格式化为字符串。

RuleBasedNumberFormat numOrdinalFormat = new RuleBasedNumberFormat(Locale.UK,
    RuleBasedNumberFormat.ORDINAL);
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Pacific/Auckland"));

String dayNumAndOrdinal = numOrdinalFormat.format(zdt.toLocalDate().getDayOfMonth());

示例输出:

第三

或者

第 4 名

等等

于 2020-01-20T03:29:30.957 回答
5

使用新的java.time包和更新的 Java switch 语句,以下内容可以轻松地将序数放置在一个月中的某一天。一个缺点是这不适合在DateFormatter类中指定的固定格式。

只需创建某种格式的一天,但包括%s%s稍后添加日期和序号。

ZonedDateTime ldt = ZonedDateTime.now();
String format = ldt.format(DateTimeFormatter
        .ofPattern("EEEE, MMMM '%s%s,' yyyy hh:mm:ss a zzz"));

现在将星期几和刚刚格式化的日期传递给辅助方法以添加序数日。


int day = ldt.getDayOfMonth();
System.out.println(applyOrdinalDaySuffix(format, day));

印刷

Tuesday, October 6th, 2020 11:38:23 AM EDT

这是辅助方法。

使用Java 14 switch 表达式可以很容易地获得序数。

public static String applyOrdinalDaySuffix(String format,
        int day) {
    if (day < 1 || day > 31)
        throw new IllegalArgumentException(
                String.format("Bad day of month (%s)", day));
    String ord = switch (day) {
        case 1, 21, 31 -> "st";
        case 2, 22 -> "nd";
        case 3, 23 -> "rd";
        default -> "th";
    };
    
    return String.format(format, day, ord);
}
于 2020-10-06T15:48:28.543 回答
3

有一种更简单、更可靠的方法可以做到这一点。您需要使用的函数是 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;

}

于 2011-08-08T19:58:13.040 回答
3

Greg 提供的解决方案的唯一问题是它不考虑大于 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";
    }
}
于 2013-02-26T20:26:48.927 回答
2

我为自己编写了一个辅助方法来获取此模式。

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
于 2018-12-12T10:04:09.640 回答
2

试试下面的功能:

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);
}
于 2020-01-27T06:18:17.907 回答
1

这是一种方法,如果找到模式,则使用正确的后缀文字更新 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 给出答案。可能更合适

于 2018-11-08T07:08:48.843 回答
1
public static String getReadableDate(final int date){
    String suffix = "th";
    switch (date){
        case 1:
        case 21:
        case 31:
            suffix = "st";
            break;
        case 2:
        case 22:
            suffix = "nd";
            break;
        case 3:
        case 23:
            suffix = "rd";
            break;
    }
    return date + suffix;
}
于 2021-03-31T07:26:29.970 回答
0

在 kotlin 你可以这样使用

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")
于 2018-02-13T07:12:33.960 回答
0

对于 Kotlin 试试这个

fun Int.ordinalAbbrev() =
        if (this % 100 / 10 == 1) "th"
        else when (this % 10) { 1 -> "st" 2 -> "nd" 3 -> "rd" else -> "th" }

它接受 int 值并像这样返回'3rd' '1st' '11th' '2nd'。所以你也可以将它用于日期格式。

用法

fun getFormatedDate(date: String): String {
        date.let {
            try {
                val parser = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
                val formatter = SimpleDateFormat("dd MMMM", Locale.getDefault())
                val dateArray = formatter.format(parser.parse(it)).split(" ").toTypedArray()
                val formatedDate = String.format(
                    "${dateArray[0]}${
                        dateArray[0].toInt().ordinalAbbrev()
                    } ${dateArray[1]}"
                )

                return formatedDate
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return date
    }
于 2021-03-26T11:54:06.323 回答
0

这是我的答案:

public String getOrdinal(int day) { 
    String ordinal; 
    switch (day % 20) { 
        case 1: 
            ordinal = "st"; 
            break; 
        case 2: 
            ordinal = "nd"; 
            break; 
        case 3: 
            ordinal = "rd"; 
            break; 
        default: 
            ordinal = day > 30 > "st" : "th"; 
    } 
    return ordinal; 
} 

只需用 20 取模,它就适用于所有日期。要获得今天的日期,您可以使用LocalDate.now().getDayOfMonth(). 或像这样度过任何一天

LocalDate.getDayOfMonth()
于 2021-10-27T16:19:00.957 回答
-3

以下方法可用于获取传递给它的日期的格式化字符串。它会使用 Java 中的 SimpleDateFormat 将日期格式化为 1st、2nd、3rd、4th ..。例如:- 2015 年 9 月 1 日

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);
        }
于 2015-09-02T04:00:08.727 回答
-4

以下是对该问题的更有效答案,而不是对样式进行硬编码。

要将日期更改为序数,您需要使用以下后缀

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

OR to spell the number add SP to the format

DD + SPTH = DDSPTH   result >>> FOURTH

在这个问题中找到我完整的答案。

于 2013-07-31T03:12:37.200 回答
-9
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";
  }
}
于 2013-01-14T04:37:58.827 回答