「11日」、「21日」、または「23日」(序数表示)を表すように月の日付をどのようにフォーマットしますか?


146

私はこれが数として私の月の日を与えるだろう知っています(112123):

SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");

しかし、月の日をどのようにフォーマットして、たとえば序数インジケータを含めるか11th21stなど23rdです。


13
参考までに、これらは序数と呼ばれます-en.wikipedia.org/wiki/Ordinal_number_(linguistics)
ocodo

5
参考までに、テーブルで回答全体を調べるのではなく、応答を構成するものを他の言語にローカライズすることはほぼ不可能です。
するThorbjörnRavnアンデルセン

2
答えはどういうわけか間違っています。私の答えを見てください。
J888 2013

2
モダンコメント:SimpleDateFormatクラスを避けることをお勧めします。それは長い間時代遅れであるだけでなく、悪名高いほど面倒でもあります。今日、私たちはjava.time最新のJavaの日付と時刻のAPIとそのをはるかに上回っていますDateTimeFormatter
Ole VV

数値API(math.tools/api/numbers)を見てください。それは数がなど、様々な言語で通貨として綴ら、異なる言語で綴ら、序、枢機卿をサポートしています
ドース

回答:


181
// 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の流体性質のに行くように固定するので、チェックしてしまうかもしれません答えにバージョン履歴をエラーを参照してください)。


36
単純なデータ形式の見落としのように感じますね。
stevevls

51
これを国際化して楽しんでください。:D
トレイカズ2014年

6
@Trejkaz質問の範囲外:)
Greg Mattes

3
このソリューションは英語のみをサポートします。ローカライズされたソリューションには、ICU4JのRuleBasedNumberFormatを使用します。
ДмитроЯковлєв

7
君たちは雑草から離れており、トーンは少し変わり始めている。その必要はありません。この質問は、国際化の複雑さについては問いませんでした。国際化は確かに議論するのに良いトピックですが、その焦点を当てた別の質問で行う必要があります。私は常にこの質問を英語でこれを達成するための迅速で汚い方法を求めるものとして解釈してきました。したがって、Javaの世界に国際化対応の優れたソリューションがない場合は、GitHubなどでプロジェクトを開始し、優れたソリューションを作成することをお勧めします。興味があれば教えてください。
Greg Mattes

51

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

8
テーブルに完全な「21st」、「23rd」、「29th」を含めると、外部化して他の言語にローカライズできます。要件となる可能性がある成功したソフトウェアの場合。
するThorbjörnRavnアンデルセン

40
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コンストラクターに提供されるパターン文字列の単一引用符を省略しながら、私はあなたの解決策を試しました。彼らがそこにいる理由がわかります。私は取得していますjava.lang.IllegalArgumentExceptionのために「違法なパターン文字、トンを。」
典型的なダニー

17

質問は少し古いです。この質問は非常にうるさいので、静的メソッドで解決したことをユーティリティとして投稿します。コピーして貼り付けて使用してください!

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

テスト用

例:メインメソッドから呼び出す!

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

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

15

私は現代の答えに貢献したいと思います。この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日

の多くの機能の1つは、java.time月の日をとして取得するのが簡単で信頼できることですint。これは、テーブルから適切なサフィックスを選択するために明らかに必要です。

単体テストも書くことをお勧めします。

PS Aと同様のフォーマッタものために使用することができるの解析のような序数を含む日付文字列1st2ndで行われていたなど、この問題の任意の秒で解析日- Javaの

リンク: Oracleチュートリアル:使用方法を説明する日時java.time


このより現代的な答えは、独自の実装をロールバックするよりも優れているため、この答えを高く上げたいと思います。
2018年

9

i18nを意識しようとすると、ソリューションはさらに複雑になります。

問題は、他の言語では、接尾辞が数字自体だけでなく、それが数える名詞にも依存する可能性があることです。たとえば、ロシア語では「2-ойдень」ですが、「2-аянеделя」です(これらは「2日目」を意味しますが、「2週目」)。これは、日のみをフォーマットする場合には当てはまりませんが、もう少し一般的なケースでは、複雑さに注意する必要があります。

良い解決策(実際に実装する時間がなかった)は、親クラスに渡す前にロケール対応のMessageFormatを適用するようにSimpleDateFormetterを拡張することだと思います。このようにして、3月のフォーマット%Mで「3-rd」、%MMで「03-rd」、%MMMで「third」を取得できるようにすることができます。このクラスの外部からは、通常のSimpleDateFormatterのように見えますが、より多くのフォーマットをサポートしています。また、このパターンが通常のSimpleDateFormetterによって誤って適用された場合、結果は正しくフォーマットされませんが、依然として読み取り可能です。


ロシア語の性別についての良い点ですが、技術的には%MMMを追加のコンテキストなしでは不可能にします。
マッド物理学者、

@Mad Physicist、%MMMは月に適用されるため、これは当てはまりません。したがって、活用する名詞はわかっています。
CAB

6

ここでの例の多くは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";
}

5

手動フォーマットに基づいた英語のみのソリューションを求める回答には満足できません。私はしばらくの間適切な解決策を探していましたが、ようやくそれを見つけました。

RuleBasedNumberFormatを使用する必要があります。完全に機能し、ロケールを尊重します。


3

これを行うには、より簡単で確実な方法があります。使用する必要がある関数は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;

}


1
この回答は文字列の解析に関するものであり、質問は文字列の生成に関するものです。ただし、どちらの方向も必要になる可能性があるため、まだ適切です。また、この回答はこの他の質問を解決します。
バジルブルク2015年

3

Gregが提供するソリューションの唯一の問題は、100を超える数が「10」の数字で終わっていないことです。たとえば、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";
    }
}

6
どのような場合、日/月のシーケンスは31日より長くなりますか?
SatanEnglish 2014年

@SatanEnglish、この静的ファクトリーメソッドの良いところは、月のサフィックスを取得するだけではなく、それ以上に使用できることです。:)
Jared Rummler

1
このメソッドは、11のst、12のnd、13のrdを返します
TheIT

@SatanEnglish。今日が私の選択したカレンダーの137番目の2月であるとすれば、あなたの質問はそれ自体で答えると思います。真面目な話ですが、どこを見ればわかるのであれば、グレゴリオ暦以外のカレンダーがたくさんあります。
マッド物理学者、

いいえ、@ TheIT、ありません。私はテストしました。必要に応じthて11、12、13 に戻ります。私はif ( hunRem - tenRem == 10 )事件がそれを確実にすると信じています。
Ole VV

2

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番目


1

次に、DateTimeFormatterパターンを更新する方法を示しますd'00'。たとえば、パターンが見つかった場合は、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による回答の場合に役立ちます。より適切かもしれない


1
クリエイティブで入り組んでいます。それがどのように機能するかを理解するのに確かに長い時間がかかりました。それは良いコードの兆候ではありません。
Ole VV

1
@ OleV.V。フィードバックのためのthx-私はそれを少し再構成したので、それは少し冗長ではありません。ちょうどあなたのアプローチを見て、私はそれが好きです!私は両方のアプローチが異なるトレードオフで有効だと思います。あなたのフォーマットの時点でそれ以上のサポートは必要ありませんが、非Javaコードでパターン定義を排除するビルダーを使用してパターンを定義する必要があります。私のアプローチでは、フォーマットの時点で追加のサポートが必要ですが、パターンを作成するためのビルダーに依存しないため、パターンを定義できる場所での柔軟性が少し高くなります。私の要件は後者を指示しました
tazmaniax 2018年

3
長所と短所の非常に素晴らしいアカウント。あなたはそれを答えに含めたいですか?ただのアイデア。
Ole VV

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

ただし、これには最低26のAPIが必要です:)
Mikkel Larsen

@MikkelLarsenこの質問はアンドロイドに関するものではなく、私はこれにJava 8を使用していました。AndroidはJava 8の大きなAPIをサポートしていません
SamHoque

@SamSakerz私の悪い:)
Mikkel Larsen

@MikkelLarsenほとんどのjava.time機能は、ThreeTen-BackportプロジェクトのJava 6およびJava 7にバックポートされています。ThreeTenABPの以前のAndroid(<26)にさらに適合。ThreeTenABPの使用方法…を参照してください。
バジルブルク

1

以下の機能を試してください:

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

古い質問に回答するとき、回答がどのように役立つかを説明するコンテキストを含めれば、他のStackOverflowユーザーにとって、特に既に回答が受け入れられている質問に対して、回答がはるかに役立つでしょう。参照:良い回答を書くにはどうすればよいですか
David Buck、

貢献していただきありがとうございます。私たちは使用していないお勧めしますDateCalendarSimpleDateFormat。これらのクラスは設計が不十分で古くなっており、特に最後のクラスは悪名高いほど厄介です。代わりに使用LocalDateしてDateTimeFormatter、両方からjava.time、現代のJavaの日付と時刻のAPIを。また、他の18の回答と比較して、あなたの回答がどのように貢献しているのか疑問です
Ole VV

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

-3

次のメソッドを使用して、渡された日付のフォーマットされた文字列を取得できます。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);
        }

-4

以下は、スタイルをハードコーディングするよりも、質問に対する効率的な回答です。

日を序数に変更するには、次の接尾辞を使用する必要があります

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

OR to spell the number add SP to the format

DD + SPTH = DDSPTH   result >>> FOURTH

この質問で私の完全な答えを見つけてください。


質問は、OracleデータベースにJavaでのフォーマットにないdocs.oracle.com/cd/B12037_01/server.101/b10759/... 日のためのJava使用のSimpleDateFormat:docs.oracle.com/javase/tutorial/i18n/format/...
Belalマズロム2015

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

endWith()は使用しないでください。間違った出力を生成する
Rahul Sharma
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.