web-dev-qa-db-ja.com

日時文字列内の日付の序数インジケーター(st、nd、rd、th)を解析する

SimpleDateFormat javadoc を確認しましたが、次のような日付形式で ordinal indicator を解析する方法が見つかりません。

 Feb 13th 2015 9:00AM

私は試した "MMM dd yyyy hh:mma"ですが、日数は正確である必要がありますか?

文字列を切り捨てることなく、SimpleDateFormatを使用して「13日」の日付を解析できますか?

20
hao

JavaのSimpleDateFormatは序数のサフィックスをサポートしていませんが、序数のサフィックスは目を見張るものです。冗長であり、簡単に削除して簡単に解析できます。

Date date = new SimpleDateFormat("MMM dd yyyy hh:mma")
    .parse(str.replaceAll("(?<=\\d)(st|nd|rd|th)", ""));

これらのシーケンスは有効な日付の他の場所には表示されないため、置換正規表現は非常に単純です。


任意の言語の任意の長さの序数標識文字をサフィックスとして追加する任意の言語を処理するには、次のようにします。

Date date = new SimpleDateFormat("MMM dd yyyy hh:mma")
    .parse(str.replaceAll("(?<=\\d)(?=\\D* \\d+ )\\p{L}+", ""));

マンダリンなどの一部の言語では序数インジケータが先頭に追加されますが、これは交互に使用しても処理できます-読者の練習問題として残しておきます:)

22
Bohemian

Java 8の回答(およびJava 6および7)(2015年にこの質問が出されたとき、SimpleDateFormatの代わりがすでに出ていたため):

    DateTimeFormatter parseFormatter = DateTimeFormatter
            .ofPattern("MMM d['st']['nd']['rd']['th'] uuuu h:mma", Locale.ENGLISH);
    LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, parseFormatter);

質問のサンプル日付で、このyiedls:

2015-02-13T09:00

形式パターンでは、[]はオプション部分を示し、''はリテラル部分を示します。したがって、パターンは、番号の後にstndrdまたはthが続く場合があることを示しています。

これをJava 6または7で使用するには、 ThreeTenバックポート が必要です。またはAndroid ThreeTenABP

これらのサフィックスは英語にとって特別であり、他の言語/ロケールでは日付と時刻を記述するために完全に他の使用法があります(AM/PMも使用しません)。他の要件がない限り、これを実装するようにしてください。英語の日付と時刻のみ。また、コンピューターまたはJVMのロケール設定とは関係なく機能するように、英語を話すロケールを明示的に指定する必要があります。

Hugomyself によって回答の最良の部分を組み合わせて、重複する質問にしようとしました。 その重複する質問 の下にはさらに多くのJava 8の回答があります。上記のコードの1つの制限は、非常に厳密な検証がないことです。Feb 13rdで回避できます。そしてFeb 13stndrdthですら。

5
Ole V.V.

誰かが便利だと思う場合:DateTimeFormatterビルダー。このフォーマッタを使用すると、序数のサフィックスを使用して英国の日付をフォーマットおよび解析できます(例:「2017年1月1日」):

public class UkDateFormatterBuilder
{
    /**
     * The UK date formatter that formats a date without an offset, such as '14th September 2020' or '1st January 2017'.
     * @return an immutable formatter which uses the {@link ResolverStyle#SMART SMART} resolver style. It has no override chronology or zone.
     */
    public DateTimeFormatter build()
    {
        return new DateTimeFormatterBuilder()
                .parseCaseInsensitive()
                .parseLenient()
                .appendText(DAY_OF_MONTH, dayOfMonthMapping())
                .appendLiteral(' ')
                .appendText(MONTH_OF_YEAR, monthOfYearMapping())
                .appendLiteral(' ')
                .appendValue(YEAR, 4)
                .toFormatter(Locale.UK);
    }

    private Map<Long, String> monthOfYearMapping()
    {
        Map<Long, String> monthOfYearMapping = new HashMap<>();
        monthOfYearMapping.put(1L, "January");
        monthOfYearMapping.put(2L, "February");
        monthOfYearMapping.put(3L, "March");
        monthOfYearMapping.put(4L, "April");
        monthOfYearMapping.put(5L, "May");
        monthOfYearMapping.put(6L, "June");
        monthOfYearMapping.put(7L, "July");
        monthOfYearMapping.put(8L, "August");
        monthOfYearMapping.put(9L, "September");
        monthOfYearMapping.put(10L, "October");
        monthOfYearMapping.put(11L, "November");
        monthOfYearMapping.put(12L, "December");
        return monthOfYearMapping;
    }

    private Map<Long, String> dayOfMonthMapping()
    {
        Map<Long, String> suffixes = new HashMap<>();
        for (int day=1; day<=31; day++)
        {
            suffixes.put((long)day, String.format("%s%s", (long) day, dayOfMonthSuffix(day)));
        }
        return suffixes;
    }

    private String dayOfMonthSuffix(final int day)
    {
        Preconditions.checkArgument(day >= 1 && day <= 31, "Illegal day of month: " + 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 class UkDateFormatterBuilderTest
{
    DateTimeFormatter formatter = new UkDateFormatterBuilder().build();

    @Test
    public void shouldFormat1stJanuaryDate()
    {
        final LocalDate date = LocalDate.of(2017, 1, 1);

        final String formattedDate = date.format(formatter);

        Assert.assertEquals("1st January 2017", formattedDate);
    }

    @Test
    public void shouldParse1stJanuaryDate()
    {
        final String formattedDate = "1st January 2017";

        final LocalDate parsedDate = LocalDate.parse(formattedDate, formatter);

        Assert.assertEquals(LocalDate.of(2017, 1, 1), parsedDate);
    }
}

PS。私はここから序数の接尾辞に対してGreg Mattesのソリューションを使用しました: Javaで「11th」、「21st」、または「23rd」を表すように月の日付をどのようにフォーマットしますか?(序数インジケーター)

2
Patrycja K

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