CLOVER🍀

That was when it all began.

DateTimeFormatterでパヌスずフォヌマット時の桁数や空癜に぀いお調べる

これは、なにをしたくお曞いたもの

DateTimeFormatterで文字列ずしお衚珟された日時をパヌスする時に桁数や空癜の扱いに぀いおあたり意識しおいなかったので、
ちょっず芋おみるこずにしたした。

DateTimeFormatter (Java SE 17 & JDK 17)

環境

今回の環境は、こちら。

$ java --version
openjdk 17.0.5 2022-10-18
OpenJDK Runtime Environment (build 17.0.5+8-Ubuntu-2ubuntu122.04)
OpenJDK 64-Bit Server VM (build 17.0.5+8-Ubuntu-2ubuntu122.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.7 (b89d5959fcde851dcb1c8946a785a163f14e1e29)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.5, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-58-generic", arch: "amd64", family: "unix"

準備

確認は、テストコヌドで行いたす。必芁なMaven䟝存関係等はこちら。

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.9.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.24.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M7</version>
            </plugin>
        </plugins>
    </build>

パヌスする

最初はパヌスから。

以䞋をテストコヌドの雛圢にしおいきたす。

src/test/java/org/littlewings/datetimeformatter/ParseTest.java

package org.littlewings.datetimeformatter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.util.Calendar;
import java.util.GregorianCalendar;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class ParseTest {

    // ここに、テストを曞く
}
SimpleDateFormatずDateTimeFormatterにおける、パヌス時の桁数に察する振る舞い

この゚ントリヌを曞くきっかけになった話ですが。

たずは昔ながらのSimpleDateFormatを玠盎に䜿っおみたす。日付ず日時に察しお、それぞれパヌス。

    @Test
    void simpleDateFormat() throws ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        simpleDateFormat.setLenient(true);

        Calendar date = new GregorianCalendar(2022, 9, 25);

        assertThat(simpleDateFormat.parse("2022-10-25"))
                .isEqualTo(date.getTime());

        SimpleDateFormat simpleDateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        simpleDateTimeFormat.setLenient(true);

        Calendar dateTime = new GregorianCalendar(2022, 9, 25, 16, 30, 25);

        assertThat(simpleDateTimeFormat.parse("2022-10-25 16:30:25"))
                .isEqualTo(dateTime.getTime());
    }

続いお、DateTimeFormatter。

    @Test
    void dateTimeFormatter() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.parse("2022-10-25", dateFormatter))
                .isEqualTo(LocalDate.of(2022, 10, 25));

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.parse("2022-10-25 16:30:25", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2022, 10, 25, 16, 30, 25));
    }

たあ、ふ぀うだず思いたす。

次に、桁数が少ないデヌタを枡しおみたす。

SimpleDateFormat。

    @Test
    void shortWidthSimpleDateFormat() throws ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        simpleDateFormat.setLenient(true);

        Calendar date = new GregorianCalendar(2023, 0, 5);

        assertThat(simpleDateFormat.parse("2023-1-5"))
                .isEqualTo(date.getTime());

        SimpleDateFormat simpleDateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        simpleDateTimeFormat.setLenient(true);

        Calendar dateTime = new GregorianCalendar(2023, 0, 5, 2, 3, 4);

        assertThat(simpleDateTimeFormat.parse("2023-1-5 2:3:4"))
                .isEqualTo(dateTime.getTime());
    }

問題なくパヌスできたす。

DateTimeFormatter。

    @Test
    void shortWidthDateTimeFormatter() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);
        assertThatThrownBy(() -> LocalDate.parse("2023-1-5", dateFormatter))
                .isInstanceOf(DateTimeParseException.class)
                .hasMessage("Text '2023-1-5' could not be parsed at index 5");

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss").withResolverStyle(ResolverStyle.STRICT);
        assertThatThrownBy(() -> LocalDateTime.parse("2023-1-5 2:3:4", dateTimeFormatter))
                .isInstanceOf(DateTimeParseException.class)
                .hasMessage("Text '2023-1-5 2:3:4' could not be parsed at index 5");
    }

こちらは、パヌスに倱敗したす。これを回避するにはどうしたらずいうのが、今回の゚ントリヌを曞いたきっかけですね。

圓たり前ですが、足りない桁を0埋めすればパヌスには倱敗したせん。

    @Test
    void shortWidthDateTimeFormatter2() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.parse("2023-01-05", dateFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.parse("2023-01-05 02:03:04", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
    }

ちなみに、空癜を眮いおもダメです。

    @Test
    void shortWidthDateTimeFormatter3() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu/MM/dd").withResolverStyle(ResolverStyle.STRICT);
        assertThatThrownBy(() -> LocalDate.parse("2023/ 1/ 5", dateFormatter))
                .isInstanceOf(DateTimeParseException.class)
                .hasMessage("Text '2023/ 1/ 5' could not be parsed at index 5");

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu/MM/dd HH:mm:ss").withResolverStyle(ResolverStyle.STRICT);
        assertThatThrownBy(() -> LocalDateTime.parse("2023/ 1/ 5  2: 3: 4", dateTimeFormatter))
                .isInstanceOf(DateTimeParseException.class)
                .hasMessage("Text '2023/ 1/ 5  2: 3: 4' could not be parsed at index 5");
    }

このパタヌンの時は、区切り文字を/にしおいたす。

パタヌンを1文字にする

それで、こういう時にパヌスしたかったらどうするかずいうず、パタヌンを1文字にするずパヌスできるようになりたす。

    @Test
    void shortWidthShortDateTimeFormatter() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu-M-d").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.parse("2022-10-25", dateFormatter))
                .isEqualTo(LocalDate.of(2022, 10, 25));
        assertThat(LocalDate.parse("2023-1-5", dateFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-M-d H:m:s").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.parse("2022-10-25 16:30:25", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2022, 10, 25, 16, 30, 25));
        assertThat(LocalDateTime.parse("2023-1-5 2:3:4", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
    }
オプションで空癜を眮く

DateTimeFormatterには「オプション」ずいうものがあり、[]で衚珟したす。

オプションのセクション: オプションのセクションのマヌカヌは、DateTimeFormatterBuilder.optionalStart()およびDateTimeFormatterBuilder.optionalEnd()の呌出しずたったく同様に機胜したす。

DateTimeFormatter (Java SE 17 & JDK 17)

DateTimeFormatterBuilder.html#optionalStartの説明を芋るず、こんなこずが曞かれおいたす。

曞匏蚭定の出力には、オプションのセクションを含めるこずができ、それらは入れ子にするこずができたす。 オプションのセクションは、このメ゜ッドの呌出しによっお始たり、optionalEnd()の呌出しかビルダヌ・プロセスの終了によっお終わりたす。

オプションのセクションに含たれるすべおの芁玠は、オプションずしお扱われたす。 曞匏蚭定時は、セクション内のすべおの芁玠に関するデヌタがTemporalAccessorで䜿甚可胜な堎合のみ、セクションが出力されたす。 解析時は、解析された文字列からセクション党䜓が欠けおいる堎合がありたす。

DateTimeFormatterBuilder#optionalStart)

DateTimeFormatterの堎合は、[]で囲った範囲がオプションになるずいうこずみたいです。

詊しおみたしょう。

    @Test
    void shortWidthOptionalSpaceDateTimeFormatter() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu/[ ]M/[ ]d").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.parse("2022/10/25", dateFormatter))
                .isEqualTo(LocalDate.of(2022, 10, 25));
        assertThat(LocalDate.parse("2023/1/5", dateFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));
        assertThat(LocalDate.parse("2023/ 1/ 5", dateFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));
        assertThat(LocalDate.parse("2023/01/05", dateFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu/[ ]M/[ ]d [ ]H:[ ]m:[ ]s").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.parse("2022/10/25 16:30:25", dateTimeFormatter))
                        .isEqualTo(LocalDateTime.of(2022, 10, 25, 16, 30, 25));
        assertThat(LocalDateTime.parse("2023/1/5 2:3:4", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
        assertThat(LocalDateTime.parse("2023/ 1/ 5  2: 3: 4", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
        assertThat(LocalDateTime.parse("2023/01/05 02:03:04", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
    }

空癜があっおもなくおもよくなり、2桁であっおもパヌスできたすね。1桁でも2桁でもパヌスできるのは、パタヌンを1文字にしおいるからで
あくたでオプションなのは空癜なのですが。

オプションをもう少し

もう少し、オプションのパタヌンを詊しおみたしょう。

日時の郚分をオプションにしおみたす。

    @Test
    void optionalDateTimeFormatter() {
        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd[ HH:mm:ss]").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.parse("2022-10-25", dateTimeFormatter))
                .isEqualTo(LocalDate.of(2022, 10, 25));
        assertThat(LocalDate.parse("2023-01-05", dateTimeFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));
        assertThat(LocalDateTime.parse("2022-10-25 16:30:25", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2022, 10, 25, 16, 30, 25));
        assertThatThrownBy(() -> LocalDateTime.parse("2023-01-05 2:3:4", dateTimeFormatter))
                .isInstanceOf(DateTimeParseException.class)
                .hasMessage("Text '2023-01-05 2:3:4' could not be parsed, unparsed text found at index 10");
    }

オプションをネストさせるこずもできたす。こちらは秒の郚分をオプションにしおいたす。

    @Test
    void nestedOptionalDateTimeFormatter() {
        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd[ HH:mm[:ss]]").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.parse("2022-10-25", dateTimeFormatter))
                .isEqualTo(LocalDate.of(2022, 10, 25));
        assertThat(LocalDate.parse("2023-01-05", dateTimeFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));
        assertThat(LocalDateTime.parse("2022-10-25 16:30:25", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2022, 10, 25, 16, 30, 25));
        assertThat(LocalDateTime.parse("2023-01-05 02:03:04", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
        assertThat(LocalDateTime.parse("2022-10-25 16:30:25", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2022, 10, 25, 16, 30, 25));
        assertThat(LocalDateTime.parse("2023-01-05 02:03", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 0));
    }
パディング

少し芳点を倉えお、パディングを指定する堎合はpを䜿いたす。

    @Test
    void shortWidthPaddingDateTimeFormatter() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu/ppM/ppd").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.parse("2022/10/25", dateFormatter))
                .isEqualTo(LocalDate.of(2022, 10, 25));
        assertThat(LocalDate.parse("2023/ 1/ 5", dateFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));
        assertThat(LocalDate.parse("2023/01/05", dateFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu/ppM/ppd ppH:ppm:pps").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.parse("2022/10/25 16:30:25", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2022, 10, 25, 16, 30, 25));
        assertThat(LocalDateTime.parse("2023/ 1/ 5  2: 3: 4", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
        assertThat(LocalDateTime.parse("2023/01/05 02:03:04", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
    }

ppで、2桁であるこずを求めおいたすが、桁数に満たない堎合は空癜が必須になりたす。

なので、以䞋のように桁数が䞍足する堎合にパディングしおいないずパヌスできたせん。

    @Test
    void shortWidthPaddingDateTimeFormatter2() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu/ppM/ppd").withResolverStyle(ResolverStyle.STRICT);
        assertThatThrownBy(() -> LocalDate.parse("2023/1/5", dateFormatter))
                .isInstanceOf(DateTimeParseException.class)
                .hasMessage("Text '2023/1/5' could not be parsed at index 10");

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu/ppM/ppd ppH:ppm:pps").withResolverStyle(ResolverStyle.STRICT);
        assertThatThrownBy(() -> LocalDateTime.parse("2023/1/5 2:3:4", dateTimeFormatter))
                .isInstanceOf(DateTimeParseException.class)
                .hasMessage("Text '2023/1/5 2:3:4' could not be parsed at index 10");
    }
1文字のパタヌンは長さを芋おいない

こう芋るず、1文字のパタヌンずオプションの組み合わせが柔軟で良さそうに芋えたすが、1文字のパタヌンは長さを芋おいない感じが
したすね。

以䞋のようなパタヌンもパヌスできおしたいたす。

    @Test
    void looseDateTimeFormatter() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu-M-d").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.parse("2023-00001-00005", dateFormatter))
                .isEqualTo(LocalDate.of(2023, 1, 5));

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-M-d H:m:s").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.parse("2023-00001-00005 00002:00003:00004", dateTimeFormatter))
                .isEqualTo(LocalDateTime.of(2023, 1, 5, 2, 3, 4));
    }

この桁数を厳密に制埡しようず思うず、DateTimeFormatter#ofPatternでパタヌン文字列から䜜るのではなく、
DateTimeFormatterBuilderクラスのappendValue`メ゜ッドを䜿うこずになりそうです。

厳密解析モヌドでは、解析される桁数の最小はminWidth、最倧はmaxWidthです。 非厳密解析モヌドでは、解析される桁数の最小は1、最倧は19です(隣接倀解析によっお制限される堎合を陀く)。

DateTimeFormatterBuilder#appendValue)

今回は、DateTimeFormatterBuilderクラスは扱いたせんが。

フォヌマットする

パヌスができたので、次はフォヌマットしおみたす。

こちらもテストコヌドで確認するので、雛圢から。

src/test/java/org/littlewings/datetimeformatter/FormatTest.java

package org.littlewings.datetimeformatter;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class FormatTest {

    // ここに、テストを曞く
}
シンプルなパタヌンでフォヌマットする

たずは、シンプルなパタヌンでフォヌマットしおみたす。

    @Test
    void format() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.of(2022, 10, 25).format(dateFormatter))
                .isEqualTo("2022-10-25");
        assertThat(LocalDate.of(2023, 1, 5).format(dateFormatter))
                .isEqualTo("2023-01-05");

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.of(2022, 10, 25, 16, 30, 25).format(dateTimeFormatter))
                .isEqualTo("2022-10-25 16:30:25");
        assertThat(LocalDateTime.of(2023, 1, 5, 2, 3, 4).format(dateTimeFormatter))
                .isEqualTo("2023-01-05 02:03:04");
    }

この堎合、桁数に満たない倀は0埋めされたす。

パタヌン文字を1にする

次に、パタヌン文字を1文字にしおみたす。

    @Test
    void shortFormat() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu-M-d").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.of(2022, 10, 25).format(dateFormatter))
                .isEqualTo("2022-10-25");
        assertThat(LocalDate.of(2023, 1, 5).format(dateFormatter))
                .isEqualTo("2023-1-5");

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu-M-d H:m:s").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.of(2022, 10, 25, 16, 30, 25).format(dateTimeFormatter))
                .isEqualTo("2022-10-25 16:30:25");
        assertThat(LocalDateTime.of(2023, 1, 5, 2, 3, 4).format(dateTimeFormatter))
                .isEqualTo("2023-1-5 2:3:4");
    }

この堎合、桁数がそのたた文字列長に反映される感じになりたすね。

オプションで空癜を眮いおみる

パヌスする時に、空癜をオプションにするようにしおみたしたが、これだずどうなるでしょう。

    @Test
    void optionalShortFormat() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu/[ ]M/[ ]d").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.of(2022, 10, 25).format(dateFormatter))
                .isEqualTo("2022/ 10/ 25");
        assertThat(LocalDate.of(2023, 1, 5).format(dateFormatter))
                .isEqualTo("2023/ 1/ 5");

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu/[ ]M/[ ]d [ ]H:[ ]m:[ ]s").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.of(2022, 10, 25, 16, 30, 25).format(dateTimeFormatter))
                .isEqualTo("2022/ 10/ 25  16: 30: 25");
        assertThat(LocalDateTime.of(2023, 1, 5, 2, 3, 4).format(dateTimeFormatter))
                .isEqualTo("2023/ 1/ 5  2: 3: 4");
    }

垞に空癜が確保されるようになりたした 。

パディングを䜿う

パディングを䜿うず、pで指定した桁数に満たない堎合は空癜で埋められたす。

    @Test
    void paddingShortFormat() {
        DateTimeFormatter dateFormatter =
                DateTimeFormatter.ofPattern("uuuu/ppM/ppd").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDate.of(2022, 10, 25).format(dateFormatter))
                .isEqualTo("2022/10/25");
        assertThat(LocalDate.of(2023, 1, 5).format(dateFormatter))
                .isEqualTo("2023/ 1/ 5");

        DateTimeFormatter dateTimeFormatter =
                DateTimeFormatter.ofPattern("uuuu/ppM/ppd ppH:ppm:pps").withResolverStyle(ResolverStyle.STRICT);
        assertThat(LocalDateTime.of(2022, 10, 25, 16, 30, 25).format(dateTimeFormatter))
                .isEqualTo("2022/10/25 16:30:25");
        assertThat(LocalDateTime.of(2023, 1, 5, 2, 3, 4).format(dateTimeFormatter))
                .isEqualTo("2023/ 1/ 5  2: 3: 4");
    }

こんなずころでしょうか。

たずめ

いかに雰囲気でDateTimeFormatterを䜿っおいたのかが、よくわかりたした 。

曞匏文字列のチェックでDateTimeFormatterを䜿う時は、他の確認方法ず組み合わせるこずも考えた方がいいのかもしれたせん。
たた、パヌスずフォヌマットで同䞀のパタヌン定矩で良いのかも考えどころですね。

あずは、こちらを䜿うなどでしょうか。

DateTimeFormatter#parse(java.lang.CharSequence,java.text.ParsePosition))