你如何格式化当天的日期来说“第11”,“第21”或“第23”(序数指标)?

时间:2010-10-24 23:56:33

标签: java date simpledateformat ordinal

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

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

但是,如何格式化当月的日期以包含ordinal indicator,比如11th21st23rd

23 个答案:

答案 0 :(得分:156)

// 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的流畅特性,此错误可能会随着时间的推移而得到修复,因此请检查{{3看到错误)。

答案 1 :(得分:47)

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

答案 2 :(得分:32)

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

答案 3 :(得分:14)

问题有点老了。因为这个问题非常嘈杂所以发布我用静态方法作为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

示例:从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

答案 4 :(得分:14)

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 :(得分:8)

如果你试图了解i18n,解决方案会变得更复杂。

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

我认为很好的解决方案(我没有时间实际实现)将扩展SimpleDateFormetter以在传递给父类之前应用Locale感知的MessageFormat。通过这种方式,您可以支持三月格式%M获得“3-rd”,%MM获得“03-rd”,%MMM获得“第三”。从外部看,这个类看起来像常规的SimpleDateFormatter,但支持更多格式。此外,如果常规SimpleDateFormetter错误地应用此模式,结果将被错误地格式化,但仍然可读。

答案 6 :(得分:7)

我想提供现代答案。 8年前提出问题时SimpleDateFormat类可以使用,但你现在应该避免使用它,因为它不仅已经过时了,而且还出了名的麻烦。请改用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 类似的格式化程序也可用于解析包含1st2nd等序数的日期字符串。这是在this question: Java - Parse date with optional seconds完成。

链接: Oracle tutorial: Date Time解释如何使用java.time

答案 7 :(得分:3)

这里的许多例子都不适用于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";
}

答案 8 :(得分:3)

使用新的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 expressions使得序数变得非常容易。

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

答案 9 :(得分:3)

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

您应该使用RuleBasedNumberFormat。它完美无缺,并且尊重Locale。

答案 10 :(得分:3)

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

答案 11 :(得分:2)

只有格雷格提供的解决方案才能解决,因为“青少年”号码的结尾不会超过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 :(得分:2)

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

}

答案 13 :(得分:1)

对于 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
    }

答案 14 :(得分:1)

尝试以下功能:

#include <stdio.h> /* needed for vsnprintf */
#include <stdarg.h> /* needed for va_list */
#include <stdlib.h> /* needed for malloc-free */
#include <string.h>
#include <sqlite3.h>


int main(int argc, char* argv[]) 
{
    sqlite3 *db;
    sqlite3_stmt *stmt;
    char *query = NULL, *zErrMsg = 0, *sql = NULL;
    char name[20] = "SAM";
    int rc, i = 100;


    /* Open database */
    rc = sqlite3_open("test.db", &db);
    if (rc) {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        while (1);
        return(0);
    }
    else {
        fprintf(stderr, "Opened database successfully..NEW\n");
    }

    /* Create a table */
    rc = sqlite3_exec(db, "create table demo (name text, age integer);", NULL, NULL, &zErrMsg);
    if (rc != SQLITE_OK)
    {
        printf("Error: %s:Unable to create the table\n", zErrMsg);
        while (1);
    }    

    asprintf(&query, "insert into demo (name) values ('%s');", name);
    /* Insert into table */
    sqlite3_prepare_v2(db, query, strlen(query), &stmt, NULL);                              /* 2 */ 
    if (sqlite3_step(stmt) == SQLITE_ROW)
    {
        printf("Inserted successfully\n");
    }
    if(stmt)
    {
        sqlite3_finalize(stmt);
        stmt = NULL;
    }   

    if(query)
    {
        free(query);
        query = NULL;   
    }

    asprintf(&query, "insert into demo (name, age) values ('%s',%d);", name, i);
    /* Insert into table */
    sqlite3_prepare_v2(db, query, strlen(query), &stmt, NULL);                              /* 2 */ 
    if (sqlite3_step(stmt) == SQLITE_ROW)
    {
        printf("Inserted successfully\n");
    }
    if(stmt)
    {
        sqlite3_finalize(stmt);
        stmt = NULL;
    }   

    if(query)
    {
        free(query);
        query = NULL;   
    }

    asprintf(&query, "insert into demo (name) values ('%s');", name);
    /* Insert into table */
    sqlite3_prepare_v2(db, query, strlen(query), &stmt, NULL);                              /* 3 */ 
    if (sqlite3_step(stmt) == SQLITE_ROW)
    {
        printf("Inserted successfully\n");
    }
    if(stmt)
    {
        sqlite3_finalize(stmt);
        stmt = NULL;
    }   

    if(query)
    {
        free(query);
        query = NULL;   
    }

    asprintf(&query, "UPDATE demo SET age = 1 WHERE age = NULL");
    stmt = NULL;
    rc = sqlite3_prepare_v2(db, query, -1, &stmt, 0);
    if (rc == SQLITE_OK)
    {
        printf("sqlite3_prepare_v2:Executed successfully\n");
    }   
    rc = sqlite3_step(stmt);                                                                    /* 3 */
    if (rc != SQLITE_DONE)
    {
        printf("ERROR inserting data: %s\n", sqlite3_errmsg(db));   
    }

    if(stmt)
    {
        sqlite3_finalize(stmt);
        stmt = NULL;
    }   

    if(query)
    {
        free(query);
        query = NULL;   
    }   
    sqlite3_close(db);      
    return 0;
}

答案 15 :(得分:1)

如果找到模式d'00',例如,使用以下方法,可以使用正确的后缀文字更新DateTimeFormatter模式。对于第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回答。可能更合适

答案 16 :(得分:1)

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

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

答案 17 :(得分: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")

答案 18 :(得分:0)

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

答案 19 :(得分:0)

如果您在Android上需要此功能,可以查看this answer

但是,它是国际化的解决方案。而且你不需要重新发明自行车;)

答案 20 :(得分:-4)

可以使用以下方法获取传递给它的日期的格式化字符串。它使用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);
        }

答案 21 :(得分:-4)

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

要将日期更改为序号,您需要使用以下suffix

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

OR to spell the number add SP to the format

DD + SPTH = DDSPTH   result >>> FOURTH

this问题中找到我已完成的答案。

答案 22 :(得分:-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";
  }
}