English 中文(简体)
你如何将一个月的哪一天格式化为“11日”、“21日”或“23日”(序数指示符)?
原标题:How do you format the day of the month to say "11th", "21st" or "23rd" (ordinal indicator)?

我知道这会给我一个月中的哪一天的数字(112123):

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

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

最佳回答
// 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的表很好,但由于重复了相同的信息,这为出现错误打开了机会。这样的错误实际上存在于7tn17tn27tn的表中(由于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";
    }
}

我想提供一个现代的答案。SimpleDateFormat类在8年前被问到这个问题时可以使用,但现在应该避免使用它,因为它不仅过时很久了,而且非常麻烦。请改用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, "第二十三");
    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

旧答案

这个代码更短,但IMHO不那么优雅。

    // 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获取是简单可靠的,这显然是从表中选择正确后缀所必需的。

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

PS类似的格式化程序也可以用于分析包含序号的日期字符串,如1st2nd等。这是在这个问题:Java-用可选秒分析日期

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

这个问题有点过时了。由于这个问题非常嘈杂,所以发布我用静态方法作为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);
    }

用于测试嘌呤

示例:从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
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)];
}

如果您试图了解i18n,那么解决方案将变得更加复杂。

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

我认为很好的解决方案(我没有时间实际实现)是在传递给父类之前扩展SimpleDateFormetter以应用Locale感知的MessageFormat。这样,你就可以支持三月格式%M获得“第三”,%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 in ICU library

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

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

下面的代码将RuleBasedNumberFormat设置为给定Locale的或dinal格式,创建java.timeZonedDateTime,然后将数字及其序号格式化为字符串。

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

String dayNumAnd或dinal = num或dinalFormat.format(zdt.toLocalDate().getDayOfMonth());

输出示例:

第三

第四

使用新的java.time包和更新的java switch语句,下面的语句很容易允许在一个月的某一天放置序号。一个缺点是,这不适用于DateFormatter类。

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

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

现在将星期几和刚刚格式化的日期传递给helper方法,以添加第几天。


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

打印

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

这是helper方法。

使用Java 14切换表达式使获取序数变得非常容易。

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

我不能满足于要求基于手动格式的纯英语解决方案的答案。我一直在寻找一个合适的解决方案,终于找到了。

您应该使用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;

}

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

我给自己写了一个助手方法来获取这方面的模式。

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

尝试以下功能:

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

对于Kotlin,试试这个

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

it takes int value and returns like this 3rd 1st 11th 2nd . So you can use it for date format also.

用法

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
    }

这里有一种方法,如果发现模式d 00,则使用正确的后缀文字更新DateTimeFormatter模式,例如,对于第1个月的第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的答案可能更合适

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

以下是我的答案:

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

Just do the modulo with 20 and it will work for all dates. To get today s day you can use LocalDate.now().getDayOfMonth(). or pass any day like this

LocalDate.getDayOfMonth()

在科特林,你可以这样使用

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




相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签