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

Ken*_*ume 131 java date ordinal simpledateformat

我知道这会给我这个月的一天,一个数(11,21,23):

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");
Run Code Online (Sandbox Code Playgroud)

但是,如何格式化月份中的日期以包含序数指标,比方说11th,21st或者23rd

Gre*_*tes 167

// 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";
    }
}
Run Code Online (Sandbox Code Playgroud)

来自@kaliatech的表很不错,但由于重复了相同的信息,因此它为错误提供了机会.这个bug实际上存在于表中7tn,17tn和,27tn(由于StackOverflow的流畅特性,这个bug可能会随着时间的推移而得到修复,因此请查看答案的版本历史记录以查看错误).

  • 玩得开心国际化.:d (41认同)
  • 它真的感觉像是简单数据格式的疏忽,不是吗? (34认同)
  • @Trejkaz问题的范围之外:) (4认同)
  • @J888 序号后缀样式是硬编码的,但这正是这个问题的上下文。 (2认同)
  • @GregMattes 谁说它在范围内?这是最终总会出现的东西。(如果软件不错。) (2认同)
  • 该解决方案仅支持英语。将ICU4J中的RuleBasedNumberFormat用于本地化解决方案。 (2认同)
  • @Trejkaz 和_这不能国际化_人群一般来说,如何“国际化”?无论如何,几乎每种语言都会有不同的_算法_。为什么必须为新语言重写算法比必须为新语言创建表要糟糕得多?您能否澄清(如果有例子更好)您正在考虑的缺点? (2认同)
  • 你们在杂草丛生中走了,语气有些转弯。没必要。这个问题没有问到国际化的复杂性。当然,国际化是一个很好的讨论话题,但是应该针对具有这个重点的另一个问题。我一直把这个问题解释为要求一种快速而肮脏的方式来用英语完成这一问题。因此,我建议,如果在Java世界中确实没有一个国际化友好的好的解决方案,那么我们可以在GitHub等上启动一个项目,并创建一个好的解决方案。让我知道您是否有兴趣。 (2认同)
  • @GregMattes 我感兴趣 (2认同)

kal*_*ech 48

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];
Run Code Online (Sandbox Code Playgroud)

或使用日历:

 Calendar c = Calendar.getInstance();
 c.setTime(date);
 int day = c.get(Calendar.DAY_OF_MONTH);
 String dayStr = day + suffixes[day];
Run Code Online (Sandbox Code Playgroud)

根据@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" };
Run Code Online (Sandbox Code Playgroud)

  • 如果您让表格包含完整的"21","23rd","29th",则可以将其外部化并本地化为其他语言.对于可能成为要求的成功软件. (7认同)

小智 34

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";
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我只是尝试了您的解决方案,同时省略了提供给 SimpleDateFormat 构造函数的模式字符串中的单引号。我现在明白他们为什么在那里了。由于“非法模式字符 t”,我收到“java.lang.IllegalArgumentException”。 (2认同)

oco*_*odo 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)];
}
Run Code Online (Sandbox Code Playgroud)


Sar*_*arz 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);
    }
Run Code Online (Sandbox Code Playgroud)

用于测试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));
        }
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)


Ole*_*.V. 10

我想提出现代的答案.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);
    }
Run Code Online (Sandbox Code Playgroud)

此代码段的输出是:

30th August
31st August
1st September
2nd September
3rd September
4th September
Run Code Online (Sandbox Code Playgroud)

老答案

这段代码较短,但恕我直言并不那么优雅.

    // 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()]);
Run Code Online (Sandbox Code Playgroud)

我刚刚运行这个片段

第23

其中一个特点java.time是,将月份的日期作为一个简单而可靠int,显然需要从表中选择正确的后缀.

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

PS类似的格式,也可用于分析含像序号日期字符串1st,2nd等等.这是在做了这样一个问题:爪哇-解析日期可选秒.

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


C.A*_*.B. 8

如果您试图了解i18n,解决方案会变得更加复杂.

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

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


siv*_*ag1 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";
}
Run Code Online (Sandbox Code Playgroud)


WJS*_*WJS 7

使用新的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"));
Run Code Online (Sandbox Code Playgroud)

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


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

Run Code Online (Sandbox Code Playgroud)

印刷

Tuesday, October 6th, 2020 11:38:23 AM EDT
Run Code Online (Sandbox Code Playgroud)

这是辅助方法。

使用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);
}
Run Code Online (Sandbox Code Playgroud)


tbi*_*gby 6

RuleBasedNumberFormat在 ICU 图书馆

我很欣赏 @Pierre-Olivier Dybman 提供的 ICU 项目库的链接(http://www.icu-project.org/apiref/icu4j/com/ibm/icu/text/RuleBasedNumberFormat.html),但是仍然需要弄清楚了解如何使用它,因此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());
Run Code Online (Sandbox Code Playgroud)

输出示例:

第三名

或者

第四名

ETC。


Pie*_*man 5

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

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