Warum erlaubt Java 8 DateTimeFormatter einen falschen Monat im ResolverStyle.STRICT-Modus?

8

Warum besteht dieser Test, während der Monatswert offensichtlich ungültig ist (13)?

%Vor%

Bei Verwendung einer temporären Abfrage wird das erwartete DateTimeParseException geworfen:

%Vor%

Was passiert, wenn keine TemporalQuery angegeben ist?

BEARBEITEN: Der 13-Wert scheint ein spezieller Wert zu sein, wie ich dank der Antwort von ΦXocê 웃 Пepeúpa ツ gelernt habe (siehe Undecimber ).

Aber die Ausnahme wird nicht einmal mit einem anderen Wert wie 50 geworfen:

%Vor%     
Virginie 23.08.2017, 12:12
quelle

3 Antworten

5

Ich habe hier ein Debugging gemacht und festgestellt, dass ein Teil des Parsing-Vorgangs darin besteht, die Felder anhand der Chronologie des Formatierers zu überprüfen.

Wenn Sie ein DateTimeFormatter erstellen, standardmäßig es verwendet ein IsoChronology , das ist verwendet, um die Datumsfelder aufzulösen. Während dieser Auflösungsphase Die Methode java.time.chrono.AbstractChronology::resolveDate wird aufgerufen.

Wenn Sie sich die Quelle , Sie sehen die folgende Logik:

%Vor%

Da die Eingabe nur die Felder Jahr und Monat enthält, gibt fieldValues.containsKey(DAY_OF_MONTH) false zurück, die Methode gibt null zurück und es wird keine andere Prüfung wie für Sie durchgeführt kann in Parsed class .

Wenn also 201750 oder 201713 ohne TemporalQuery analysiert wird, wird aufgrund der obigen Logik keine zusätzliche Überprüfung durchgeführt, und die Methode parse gibt ein java.time.format.Parsed -Objekt zurück, wie Sie am folgender Code:

%Vor%

Die Ausgabe ist:

  

class java.time.format.Parsed
  {Jahr = 2017, MonthOfYear = 50}, ISO

Beachten Sie, dass der Typ des zurückgegebenen Objekts java.time.format.Parsed ist und das Drucken die Felder anzeigt, die analysiert wurden (Jahr und Monat).

Wenn Sie parse mit einem TemporalQuery aufrufen, wird das Objekt Parsed jedoch an die Abfrage übergeben und ihre Felder werden validiert (natürlich hängt es von der Abfrage ab, aber die eingebauten APIs validieren immer ).

Im Falle von YearMonth::from wird geprüft, ob das Jahr und der Monat gültig sind, indem die entsprechenden ChronoField verwendet werden ( MONTH_OF_YEAR und YEAR ) und das Feld" Monat "akzeptiert nur Werte von 1 bis 12 .

Deshalb ruft nur der Aufruf von parse(value) keine Ausnahme auf, sondern ruft mit TemporalQuery auf.

Nur um die obige Logik zu überprüfen, wenn alle Datumsfelder (Jahr, Monat und Tag) vorhanden sind:

%Vor%

Dies wirft:

  

Ausnahme im Thread "main" java.time.format.DateTimeParseException: Text '20175010' konnte nicht analysiert werden: Ungültiger Wert für MonthOfYear (gültige Werte 1 - 12): 50

Da alle Datumsfelder vorhanden sind, gibt fieldValues.containsKey(DAY_OF_MONTH) true zurück und prüft nun, ob es ein gültiges Datum ist (mit resolveYMD Methode ).

    
user7605325 23.08.2017, 13:40
quelle
3

Der Monat 13 heißt: Undecimber

Der gregorianische Kalender, den viele von uns verwenden, erlaubt nur 12 Monate, aber Java bietet Unterstützung für Kalender, die dreizehn Monate erlauben, also hängt es davon ab, von welchem ​​Kalendersystem Sie sprechen

Der tatsächliche Höchstwert des Felds MONTH ist beispielsweise 12 in einigen Jahren und 13 in anderen Jahren im hebräischen -Kalender System. Also der Monat 13 ist gültig

    
quelle
0

Es ist ein wenig seltsam, dass eine Ausnahme nicht ausgelöst wird, wenn parse ohne eine gegebene TemporalQuery aufgerufen wird. Ein Teil der Dokumentation für das einzelne Argument parse method:

  

Dies analysiert den gesamten Text, der ein temporäres Objekt erzeugt. In der Regel ist es sinnvoller, parse (CharSequence, TemporalQuery) zu verwenden. Das Ergebnis dieser Methode ist TemporalAccessor, das aufgelöst wurde und grundlegende Validierungsprüfungen anwendet, um ein gültiges Datum-Uhrzeit-Datum sicherzustellen.

Beachten Sie, dass es "normalerweise sinnvoller ist, parse (CharSequence, TemporalQuery) zu verwenden". In Ihren Beispielen gibt parse ein java.time.format.Parsed -Objekt zurück, das eigentlich nur für das Erstellen eines anderen TemporalAccessor verwendet wird.

Beachten Sie, dass beim Versuch, YearMonth aus dem zurückgegebenen Wert zu erstellen, eine Ausnahme ausgelöst wird:

%Vor%

wirft

%Vor%

Dokumentation für Parsed :

  

Ein Speicher von geparsten Daten.

     

Diese Klasse wird beim Parsen verwendet, um die Daten zu sammeln. Ein Teil des Analyseprozesses beinhaltet die Handhabung von optionalen Blöcken und mehrere Kopien der Daten werden erstellt, um das notwendige Zurückverfolgen zu unterstützen.

     

Sobald die Analyse abgeschlossen ist, kann diese Klasse als resultierender TemporalAccessor verwendet werden. In den meisten Fällen wird es nur angezeigt, sobald die Felder aufgelöst wurden.

     

Seit: 1.8

     

@implSpecThis-Klasse ist ein veränderbarer Kontext, der aus einem einzelnen Thread verwendet werden soll. Die Verwendung der Klasse ist Thread-sicher innerhalb der Standardanalyse, da eine neue Instanz dieser Klasse automatisch für jede Syntaxanalyse erstellt wird und die Analyse single-threaded ist

    
lucasvw 23.08.2017 13:07
quelle