簡體   English   中英

您如何格式化一個月中的某一天以表示“11 號”、“21 號”或“23 號”(序數指示符)?

[英]How do you format the day of the month to say "11th", "21st" or "23rd" (ordinal indicator)?

我知道這會給我一個月中的某一天作為數字( 11 , 21 , 23 ):

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

但是您如何格式化一個月中的某一天以包含一個序數指示符,比如11th21st 23rd23rd

// 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, "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等)的日期字符串。這是在這個問題中完成的Java-Parse date with optional seconds

鏈接: Oracle 教程:解釋如何使用java.time日期時間

問題有點老了。 由於這個問題非常嘈雜,因此將我使用靜態方法解決的問題發布為實用程序。 只需復制、粘貼和使用它!

 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-ая неделя”(這些意思是“第 2 天”,而是“第 2 周”)。 如果我們只格式化幾天,這不適用,但在更通用的情況下,您應該意識到復雜性。

我認為不錯的解決方案(我沒有時間實際實現)是在傳遞給父類之前擴展 SimpleDateFormetter 以應用 Locale-aware MessageFormat。 這樣你就可以支持讓說三月格式 %M 獲得“3-rd”, %MM 獲得“03-rd”和 %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 它完美運行,並且尊重語言環境。

ICU 庫中的RuleBasedNumberFormat

我很欣賞@Pierre-Olivier Dybman ( http://www.icu-project.org/apiref/icu4j/com/ibm/icu/text/RuleBasedNumberFormat.html ) 提供的 ICU 項目庫的鏈接,但仍然需要計算了解如何使用它,因此下面是RuleBasedNumberFormat用法的示例。

它只會格式化單個數字而不是整個日期,因此,如果您要以以下格式查找日期,則需要構建一個組合字符串:例如,2 月 3 日星期一。

下面的代碼將RuleBasedNumberFormat設置為給定 Locale 的 Ordinal 格式,創建一個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());

示例輸出:

第三名

或者

第四名

等等。

使用新的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);
}

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

有一種更簡單且可靠的方法可以做到這一點。 您需要使用的函數是 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;

}

試試下面的功能:

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

這是一種方法,如果它找到模式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 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

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

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

不過,它是國際化的解決方案。 而且您不需要重新發明自行車;)

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 = "th"; 
    } 
    return ordinal; 
} 

只需對 20 進行模數運算,它就適用於所有日期。 要獲得今天,您可以使用LocalDate.now().getDayOfMonth() 或者像這樣度過任何一天

LocalDate.getDayOfMonth()

對於 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
    }

以下方法可用於獲取傳遞給它的日期的格式化字符串。 它會將日期格式化為 1st,2nd,3rd,4th .. 在 Java 中使用 SimpleDateFormat 。 例如:- 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);
        }

以下是對該問題的更有效的回答,而不是對樣式進行硬編碼。

要將日期更改為序號,您需要使用以下后綴

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

OR to spell the number add SP to the format

DD + SPTH = DDSPTH   result >>> FOURTH

這個問題中找到我完整的答案。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM