Próbuję przeanalizować następującą datę, aby uzyskać błąd w czasie wykonywania:

java.time.format.DateTimeParseException: Text '2019-11-21-05:00' could not be parsed, unparsed text found at index 10

Mój wkład:

String inpDate = "2019-11-20-05:00"

Próbowałem również następujących formatów daty, ale bez powodzenia.

yyyy-MM-ddZ

yyyy-MM-dd Z

Kod:

public static final String DATE_FORMAT = "yyyy-MM-dd";
public static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);

LocalDate localDate = LocalDate.parse(inpDate, dateFormatter);
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());

Jak mogę sprawić, aby moje dane wejściowe zostały poprawnie przeanalizowane?

0
Vik K 20 grudzień 2019, 01:51

3 odpowiedzi

Użyj następującego formatu:

public static final String DATE_FORMAT = "yyyy-MM-ddZZZZZ";

Ma 5 razy literę „Z”. Aby przeanalizować przesunięcie strefy czasowej za pomocą dwukropka, musisz 5 razy podać literę „Z”. Może to być nieco ukryte w dokumentacji Javadoc.

Z Javadoc:

Przesunięcie Z: formatuje przesunięcie na podstawie liczby liter wzorca.

Jedna, dwie lub trzy litery powodują wyświetlenie godziny i minuty bez dwukropka, np. „+0130”. Wyjście będzie '+0000', gdy przesunięcie wynosi zero.

Cztery litery wytwarzają pełną formę zlokalizowanego przesunięcia, odpowiednik czterech liter offset-o. Wyjście będzie odpowiedniego tekstu zlokalizowanego tekstu, jeśli przesunięcie jest zero.

Pięć liter to godzina, minuta, z opcjonalną sekundą, jeśli jest różna od zera, z dwukropkiem.

Wyprowadza 'Z', jeśli przesunięcie wynosi zero. Sześć lub więcej liter generuje IllegalArgumentException.

1
Erwin Bolwidt 20 grudzień 2019, 02:03
        String inpDate = "2019-11-20-05:00";
        System.out.println(LocalDate.parse(inpDate, DateTimeFormatter.ofPattern("yyyy-MM-dd-hh:mm")));

Wydrukuje 2019-11-20.

0
SephB 20 grudzień 2019, 02:02
To jest niepoprawne. Jeśli 05:00 to pora dnia, potrzebujesz wielkich liter HH jako godziny dnia. Ale prawdopodobnie tak nie jest. Biorę -05:00 za przesunięcie UTC. Data bez pory dnia i z przesunięciem jest osobliwa, ale czasami jest używana, nie bardzo rozumiem po co.
 – 
Ole V.V.
20 grudzień 2019, 09:26
Godzina (lub przesunięcie czasowe, jeśli tak jest) nie wydawała się potrzebna, ponieważ chcieli tylko daty. Więc po prostu zrobiłem coś, co by to przeanalizowało, chociaż HH jest prawdopodobnie lepsze.
 – 
SephB
20 grudzień 2019, 19:13

To łatwiejsze niż myślisz. Formater, którego potrzebujesz, jest wbudowany. Nie musisz więc walczyć z pisaniem własnego ciągu wzorca formatu.

    String inpDate = "2019-11-20-05:00";
    LocalDate localDate = LocalDate.parse(inpDate, DateTimeFormatter.ISO_OFFSET_DATE);
    System.out.println(Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));

Wyjście w mojej strefie czasowej:

śr 20 lis 00:00:00 EST 2019

Zakładam, że dokonujesz konwersji tylko na Date, ponieważ potrzebujesz go do starszego interfejsu API, którego nie możesz teraz uaktualnić do java.time. W przeciwnym razie nie powinieneś używać Date, ale trzymaj się nowoczesnego API.

Początek dnia w Twojej strefie czasowej czy przesunięcie podane w ciągu znaków?

Edytuj: Basil Bourque w komentarzu zadał to bardzo kompetentne pytanie:

Być może byłoby to bardziej zgodne z intencją wydawcy tego ciąg wejściowy, aby uzyskać pierwszą chwilę dnia, jak widać w tym przesunięciu 5 godzin za UTC. Czy można analizować jako OffsetDateTime z domyślną porą dnia do pierwszej chwili dnia (00:00:00)? (A następnie przekonwertuj na java.util.Date, jeśli jest to wymagane).

To zdecydowanie prawda. Zdecydowałem się podać kod, który daje wynik, który moim zdaniem próbował uzyskać kod w pytaniu. Jeśli rozumiemy 2019-11-20-05:00 jako nieokreślony czas w przedziale półotwartym od 2019-11-20T00:00-05:00 do 2019-11-21T00:00-05:00, to dla niektórych przesunięć i niektórych domyślnych strefy czasowe wynik z powyższego fragmentu kodu będzie faktycznie leżeć poza tym interwałem (zazwyczaj przed nim, w skrajnych przypadkach może również przypadać po nim). Tak więc jest to zgodne z pytaniem (jeśli dobrze zrozumiałem) i nieprawdziwe w stosunku do wydawcy oryginalnego ciągu. Jeśli zamiast tego chcemy rozpocząć dzień od przesunięcia UTC podanego w ciągu, zrobiłbym to tak:

    TemporalAccessor parsed = DateTimeFormatter.ISO_OFFSET_DATE.parse(inpDate);
    Instant startOfDayAtSpecifiedOffset = LocalDate.from(parsed)
            .atStartOfDay(ZoneOffset.from(parsed))
            .toInstant();
    System.out.println(Date.from(startOfDayAtSpecifiedOffset));

Wyjście w mojej strefie czasowej:

śr 20 lis 06:00:00 CET 2019

W listopadzie jestem na offsecie +01:00, dlatego tutaj jest godzina 06:00, kiedy dzień zaczyna się o offsecie -05:00. Aby zilustrować, że wybór strefy czasowej lub przesunięcia może mieć duże znaczenie, oto wynik uruchomienia drugiego fragmentu z domyślną strefą czasową Pacyfik/Kiritimati:

śr 20 lis 19:00:00 LINT 2019

Lub w strefie czasowej Pacyfik/Pago_Pago:

wt lis 19 18:00:00 SST 2019

Więc ostrożnie wybierz to, czego naprawdę chcesz.

Co poszło nie tak w swoim kodzie?

Po pierwsze, LocalDate.parse() i podobne metody parsowania nalegają na parsowanie całego łańcucha lub wyrzucą wyjątek, który widziałeś wspominając o unparsed text found at index (some index) (możesz użyć przeciążonej metody DateTimeFormatter.parse​(CharSequence, ParsePosition) do parsowania tylko jak to możliwe ciągu bez tego wyjątku).

Po drugie, jeden Z w ciągu wzorca formatu pasuje do przesunięcia bez dwukropka, więc -0500, a nie -05:00.

Link: Dokumentacja DateTimeFormatter.parse​(CharSequence, ParsePosition)

1
Ole V.V. 20 grudzień 2019, 23:19