你怎样把一个月的某一天格式化为“11号”、“21号”或“23号”(序数标识) ?

我知道这会给我一个数字(112123) :

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

但是,你如何格式化一个月的日子,以包括一个 序数标识,说 11th21st23rd

183688 次浏览

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" };
// 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 的表很不错,但是由于相同的信息是重复的,所以它打开了 bug 的机会。这样的错误实际上存在于 7tn17tn27tn的表中(由于 StackOverflow 的流体特性,这个错误可能会随着时间的推移而得到修复,因此请检查 版本历史上的答案以查看错误)。

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

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

}

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

格雷格提供的解决方案的唯一问题是,它没有解释以“青少年”数字结尾的大于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";
}
}

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

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

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


OR to spell the number add SP to the format


DD + SPTH = DDSPTH   result >>> FOURTH

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

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

问题是,在其他语言中,后缀可能不仅取决于数字本身,而且还取决于它所计算的名词。例如,在俄语中,它是“2-”,但是是“2-”(这意味着“第二天”,但是“第二周”)。如果我们只是在几天内进行格式化,那么这不适用,但是在更一般的情况下,您应该注意复杂性。

我认为好的解决方案(我没有时间实际实现)是在传递给父类之前扩展 SimpleDateFormetter 以应用支持语言环境的 MessageFormat。这样,您就能够支持3月格式% M 获得“3-rd”,% MM 获得“03-rd”,% MMM 获得“ Third”。从外部看,这个类类似于常规的 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";
}

下面的方法可用于获取传递给它的日期的格式化字符串。它会把日期格式化为1,2,3,4。.在 Java 中使用 SimpleDateFormat。- 二零一五年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);
}

这个问题有点老套。由于这个问题是非常嘈杂的,所以张贴我所做的解决静态方法作为一个 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

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

你应该使用 基于规则的数字格式。它工作得很好,并且尊重现场。

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

我愿意贡献现代的答案。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等。这是在 这个问题: < em > 带有可选秒的 Java-Parse 日期 中完成的。

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

这里有一种方法,如果找到模式 d'00',它可以使用正确的后缀文本更新 DateTimeFormatter 模式,例如,对于第一个月的某一天,它将被替换为 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 更合适

我自己编写了一个 helper 方法来获取这个问题的模式。

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

重症监护室图书馆 RuleBasedNumberFormat

我很欣赏@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());

输出示例:

第三名

或者

四年级

等等。

试试以下功能:

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

使用新的 爪哇时间包和新的 Java switch 语句,下面很容易地允许在每个月的某一天放置序号。一个缺点是,这不适用于在 日期格式化类中指定的罐装格式。

只需创建某种格式的一天,但包括 %s%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);
}

科特林试试这个

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

它接受 int 值并返回类似于 “三”“一”“十一”“二”的值。 因此,您也可以将其用于日期格式。

用法

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

只要做20的模块,它将工作的所有日期。 你可以使用 LocalDate.now().getDayOfMonth()来得到今天的情况。 或者像这样度过任何一天

LocalDate.getDayOfMonth()