web-dev-qa-db-ja.com

新しい日時APIを使用して日付をフォーマットする

私は新しい日時APIで遊んでいましたが、これを実行するとき:

public class Test {         
    public static void main(String[] args){
        String dateFormatted = LocalDate.now()
                                        .format(DateTimeFormatter
                                              .ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println(dateFormatted);
    }
}

投げる:

Exception in thread "main" Java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
    at Java.time.LocalDate.get0(LocalDate.Java:680)
    at Java.time.LocalDate.getLong(LocalDate.Java:659)
    at Java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.Java:298)
    at Java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.Java:2543)
    at Java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.Java:2182)
    at Java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.Java:1745)
    at Java.time.format.DateTimeFormatter.format(DateTimeFormatter.Java:1719)
    at Java.time.LocalDate.format(LocalDate.Java:1685)
    at Test.main(Test.Java:23)

LocalDateクラスのソースコードを見ると、次のように見えます。

  private int get0(TemporalField field) {
        switch ((ChronoField) field) {
            case DAY_OF_WEEK: return getDayOfWeek().getValue();
            case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1;
            case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
            case DAY_OF_MONTH: return day;
            case DAY_OF_YEAR: return getDayOfYear();
            case Epoch_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead");
            case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1;
            case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
            case MONTH_OF_YEAR: return month;
            case PROLEPTIC_MONTH: throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead");
            case YEAR_OF_ERA: return (year >= 1 ? year : 1 - year);
            case YEAR: return year;
            case ERA: return (year >= 1 ? 1 : 0);
        }
        throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    }

ドキュメントで説明されているように:

このメソッドは、クラスのドキュメントで説明されているように、文字と記号の単純なパターンに基づいてフォーマッターを作成します。

そして、これらの文字はすべて 定義済み です。

では、なぜDateTimeFormatter.ofPatternでパターン文字を使用できないのですか?

95
user2336315

LocalDateは、DateTimeではなく、単なる日付を表します。したがって、LocalDateをフォーマットする場合、「HH:mm:ss」は意味がありません。代わりにLocalDateTimeを使用します(日付と時刻の両方を表現したい場合)。

170
James_D

@James_Dの正解に次の詳細を追加したいと思います。

背景:ほとんどの日時ライブラリ(JavaのJava.util.Calendar 、. Net-DateTimeまたはDateも参照) JavaScript(PerlのDateTime)は、万能で汎用的な時間型(ドイツ語では「eierlegende Wollmilchsau」という詩的表現)の概念に基づいています。この設計では、サポートされていないフィールドは存在できません。しかし、価格は高いです。多くの時間の問題は、このような柔軟性のないアプローチでは適切に処理できません。これは、すべての種類の時間オブジェクトの共通分母を見つけることが不可能なためです。

JSR-310は別の方法を選択しました。つまり、サポートされている組み込みフィールドのタイプ固有のセットで構成されるさまざまな時間タイプを許可します。自然な結果は、すべての可能なフィールドがすべてのタイプでサポートされるわけではないということです(そして、ユーザーは独自の専門フィールドを定義することさえできます)。また、サポートされているフィールドの特定のセットに対して、タイプTemporalAccessorのすべてのオブジェクトを プログラムで要求 することもできます。 LocalDateの場合:

•DAY_OF_WEEK 
•ALIGNED_DAY_OF_WEEK_IN_MONTH 
•ALIGNED_DAY_OF_WEEK_IN_YEAR 
•DAY_OF_MONTH 
•DAY_OF_YEAR 
•Epoch_DAY 
•ALIGNED_WEEK_OF_MONTH 
•ALIGNED_WEEK_OF_YEAR 
•MONTH_OF_YEAR 
•PROLEPTIC_MONTH 
•YEAR_OF_ERA 
•YEAR 
•ERA 

UnsupportedTemporalTypeExceptionの問題を説明するHOUR_OF_DAYフィールドはありません。 JSR-310 - パターンシンボルのフィールドへのマッピング を見ると、シンボルHがサポートされていないHOUR_OF_DAYにマッピングされていることがわかります。

/** Map of letters to fields. */  
private static final Map<Character, TemporalField> FIELD_MAP = new HashMap<>();
static {
  FIELD_MAP.put('G', ChronoField.ERA);
  FIELD_MAP.put('y', ChronoField.YEAR_OF_ERA);
  FIELD_MAP.put('u', ChronoField.YEAR);
  FIELD_MAP.put('Q', IsoFields.QUARTER_OF_YEAR);
  FIELD_MAP.put('q', IsoFields.QUARTER_OF_YEAR);
  FIELD_MAP.put('M', ChronoField.MONTH_OF_YEAR);
  FIELD_MAP.put('L', ChronoField.MONTH_OF_YEAR);
  FIELD_MAP.put('D', ChronoField.DAY_OF_YEAR);
  FIELD_MAP.put('d', ChronoField.DAY_OF_MONTH);
  FIELD_MAP.put('F', ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH);
  FIELD_MAP.put('E', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('c', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('e', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('a', ChronoField.AMPM_OF_DAY);
  FIELD_MAP.put('H', ChronoField.HOUR_OF_DAY);
  FIELD_MAP.put('k', ChronoField.CLOCK_HOUR_OF_DAY);
  FIELD_MAP.put('K', ChronoField.HOUR_OF_AMPM);
  FIELD_MAP.put('h', ChronoField.CLOCK_HOUR_OF_AMPM);
  FIELD_MAP.put('m', ChronoField.MINUTE_OF_HOUR);
  FIELD_MAP.put('s', ChronoField.SECOND_OF_MINUTE);
  FIELD_MAP.put('S', ChronoField.NANO_OF_SECOND);
  FIELD_MAP.put('A', ChronoField.MILLI_OF_DAY);
  FIELD_MAP.put('n', ChronoField.NANO_OF_SECOND);
  FIELD_MAP.put('N', ChronoField.NANO_OF_DAY);    
}

このフィールドマッピングは、フィールドが具象型によってサポートされることを意味するものではありません。解析はいくつかのステップで行われます。フィールドマッピングは最初のステップにすぎません。次に、2番目のステップはTemporalAccessor型の生オブジェクトに解析します。最後に、デリゲートをターゲットタイプ(ここではLocalDate)に解析し、解析された中間オブジェクトのすべてのフィールド値を受け入れるかどうかを決定させます。

33
Meno Hochschild

私にとって適切なクラスは ZonedDateTime で、これには時間とタイムゾーンの両方が含まれます。

LocalDateには時間情報がないため、UnsupportedTemporalTypeException: Unsupported field: HourOfDayを取得します。

LocalDateTimeを使用できますが、タイムゾーン情報がないため、それにアクセスしようとすると(定義済みのフォーマッターのいずれかを使用しても)UnsupportedTemporalTypeException: Unsupported field: OffsetSecondsを取得します。

3
isapir